use crate::cli::{Cli, TypoType};
use crate::keyvalue::{Key, Value};
use crate::typosjsonline::TyposJsonLine;
use fluent_i18n::t;
use std::collections::HashMap;
use std::error::Error;
use std::fs::File;
use std::io::{self, BufRead};
use std::path::Path;
use std::process::exit;
#[derive(Debug)]
pub struct THashMap {
hashmap: HashMap<Key, Vec<Value>>,
}
impl Default for THashMap {
fn default() -> Self {
Self::new()
}
}
impl THashMap {
fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
where
P: AsRef<Path>,
{
let file = File::open(filename)?;
Ok(io::BufReader::new(file).lines())
}
#[must_use]
pub fn new() -> Self {
let hashmap: HashMap<Key, Vec<Value>> = HashMap::new();
THashMap {
hashmap,
}
}
pub fn insert(&mut self, typojson: TyposJsonLine, cli: &Cli) {
if !typojson.is_excluded(cli) {
let typo = typojson.typo;
let corrections = typojson.corrections;
let key = Key {
typo,
corrections,
};
if let Some(v) = self.hashmap.get_mut(&key) {
let value = Value {
path: typojson.path,
line_num: typojson.line_num,
byte_offset: typojson.byte_offset,
};
v.push(value);
} else {
let mut v: Vec<Value> = Vec::new();
let value = Value {
path: typojson.path,
line_num: typojson.line_num,
byte_offset: typojson.byte_offset,
};
v.push(value);
self.hashmap.insert(key, v);
}
} else if cli.debug {
if typojson.is_file_excluded(cli) {
eprintln!("{}", t!("thashmap-file-excluded", {"file" => typojson.path}));
} else if typojson.is_typo_excluded(cli) {
eprintln!("{}", t!("thashmap-typo-excluded", {"typo" => typojson.typo}));
} else if typojson.is_correction_excluded(cli) {
eprintln!(
"{}",
t!("thashmap-correction-excluded", { "correction" => format!("{:?}", typojson.corrections), "typo" => typojson.typo})
);
}
}
}
pub fn read_typos_file(mut self, cli: &Cli) -> Result<Self, Box<dyn Error>> {
match THashMap::read_lines(&cli.filename) {
Ok(lines) => {
for line in lines.map_while(Result::ok) {
let typojson = serde_json::from_str::<TyposJsonLine>(&line)?;
if typojson.type_id == "typo" {
self.insert(typojson, cli);
}
}
}
Err(e) => {
eprintln!("{}", t!("thashmap-error-file", {"e" => e.to_string()}));
exit(1);
}
}
Ok(self)
}
pub fn list_typos(&self, cli: &Cli) {
let Some(only_list_typos) = &cli.only_list_typos else {
return;
};
for (key, values) in &self.hashmap {
let files_string = t!("thashmap-file-count", {"count" => values.len()}).to_string();
let correctable = key.is_typo_correctable(cli);
let should_print = matches!(
(correctable, only_list_typos),
(true, TypoType::All | TypoType::Corrected) | (false, TypoType::All | TypoType::NotCorrected)
);
if !should_print {
continue;
}
if correctable {
println!("'{}' -> {:?}) {}", key.typo, key.corrections, files_string);
} else {
println!(
"\t{}",
t!("thashmap-wont-correct", {"typo" => key.typo, "correction" => format!("{:?}",key.corrections), "files" => files_string})
);
}
if cli.details {
for v in values {
v.print_value_details();
}
println!();
}
}
}
pub fn correct_typos(&self, cli: &Cli) {
for (key, values) in &self.hashmap {
if key.is_typo_correctable(cli) {
let files = values.iter().map(|v| v.path.clone()).collect();
key.run_sed(&files, cli);
key.run_git_commit(cli);
} else if cli.details {
println!();
println!(
"{}\n{}",
t!("thashmap-typo-not-corrected", {"typo" => key.typo, "correction" => format!("{:?}",key.corrections)}),
t!("thashmap-typo-look-carefully")
);
for v in values {
v.print_value_details();
}
println!();
}
}
}
}