typos_git_commit/
thashmap.rs1use crate::cli::Cli;
2use crate::keyvalue::{Key, Value};
3use crate::typosjsonline::TyposJsonLine;
4use std::collections::HashMap;
5use std::error::Error;
6use std::fs::File;
7use std::io::{self, BufRead};
8use std::path::Path;
9use std::process::exit;
10
11#[derive(Debug)]
15pub struct THashMap {
16 thm: HashMap<Key, Vec<Value>>,
17}
18
19impl THashMap {
20 fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
21 where
22 P: AsRef<Path>,
23 {
24 let file = File::open(filename)?;
25 Ok(io::BufReader::new(file).lines())
26 }
27
28 pub fn new() -> Self {
29 let thm: HashMap<Key, Vec<Value>> = HashMap::new();
30 THashMap {
31 thm,
32 }
33 }
34
35 pub fn insert(&mut self, typojson: TyposJsonLine, cli: &Cli) {
38 if !typojson.is_excluded(cli) {
39 let typo = typojson.typo;
40 let corrections = typojson.corrections;
41 let key = Key {
42 typo,
43 corrections,
44 };
45
46 match self.thm.get_mut(&key) {
47 Some(v) => {
48 let value = Value {
51 path: typojson.path,
52 line_num: typojson.line_num,
53 byte_offset: typojson.byte_offset,
54 };
55 v.push(value);
56 }
57 None => {
58 let mut v: Vec<Value> = Vec::new();
61 let value = Value {
62 path: typojson.path,
63 line_num: typojson.line_num,
64 byte_offset: typojson.byte_offset,
65 };
66 v.push(value);
67 self.thm.insert(key, v);
68 }
69 }
70 } else if cli.debug {
71 if typojson.is_file_excluded(cli) {
74 eprintln!("File {} excluded", typojson.path);
75 } else if typojson.is_typo_excluded(cli) {
76 eprintln!("Typo {} excluded.", typojson.typo);
77 } else if typojson.is_correction_excluded(cli) {
78 eprintln!("Correction {:?} for typo {} excluded.", typojson.corrections, typojson.typo);
79 }
80 }
81 }
82
83 pub fn read_typos_file(mut self, cli: &Cli) -> Result<Self, Box<dyn Error>> {
85 match THashMap::read_lines(&cli.filename) {
86 Ok(lines) => {
87 for line in lines.map_while(Result::ok) {
89 let typojson = serde_json::from_str::<TyposJsonLine>(&line)?;
90 if typojson.type_id == "typo" {
91 self.insert(typojson, cli);
92 }
93 }
94 }
95 Err(e) => {
96 eprintln!("Error reading file: {e}");
97 exit(1);
98 }
99 }
100 Ok(self)
101 }
102
103 pub fn list_typos(&self, cli: &Cli) {
105 for (key, values) in &self.thm {
106 let values_len = values.len();
107 let files_string = match values_len {
108 1 => "(1 file)".to_string(),
109 _ => format!("({} files)", values_len),
110 };
111
112 if key.is_typo_correctable(cli) {
116 println!("'{}' -> {:?}) {}", key.typo, key.corrections, files_string);
117 } else {
118 println!("Won't correct '{}' -> {:?}) {}", key.typo, key.corrections, files_string);
119 }
120
121 if cli.details {
122 for v in values {
123 println!("\t{} at line {} at offset {}", v.path, v.line_num, v.byte_offset);
124 }
125 println!();
126 }
127 }
128 }
129
130 pub fn correct_typos(&self, cli: &Cli) {
132 for (key, values) in &self.thm {
133 if key.is_typo_correctable(cli) {
137 let mut files: Vec<String> = Vec::new();
138 for v in values {
139 files.push(v.path.clone());
140 }
141
142 key.run_sed(&files, cli);
143 key.run_git_commit(cli);
144 } else if cli.details {
145 println!();
146 println!("Typo '{}' will not be corrected into {:?}\nYou should look carefully at file(s):", key.typo, key.corrections);
147 for v in values {
148 println!("\t{} at line {} at offset {}", v.path, v.line_num, v.byte_offset);
149 }
150 println!();
151 }
152 }
153 }
154}