1use anyhow::bail;
2use ignore::WalkState;
3use std::sync::mpsc;
4
5use crate::{
6 replace::{self, calculate_statistics, format_replacement_results},
7 search::SearchResult,
8 validation::{
9 SearchConfiguration, SimpleErrorHandler, ValidationResult, validate_search_configuration,
10 },
11};
12
13pub fn find_and_replace(search_config: SearchConfiguration<'_>) -> anyhow::Result<String> {
14 let mut error_handler = SimpleErrorHandler::new();
15 let result = validate_search_configuration(search_config, &mut error_handler)?;
16 let searcher = match result {
17 ValidationResult::Success(searcher) => searcher,
18 ValidationResult::ValidationErrors => {
19 bail!("{}", error_handler.errors_str().unwrap());
20 }
21 };
22
23 let (results_sender, results_receiver) = mpsc::channel::<Vec<SearchResult>>();
24
25 let sender_clone = results_sender.clone();
26 searcher.walk_files(None, move || {
27 let sender = sender_clone.clone();
28 Box::new(move |mut results| {
29 if let Err(file_err) = replace::replace_in_file(&mut results) {
30 log::error!("Found error when performing replacement: {file_err}");
31 }
32 if sender.send(results).is_err() {
33 WalkState::Quit
35 } else {
36 WalkState::Continue
37 }
38 })
39 });
40
41 drop(results_sender);
42
43 let all_results = results_receiver.into_iter().flatten();
44 let stats = calculate_statistics(all_results);
45
46 for error in &stats.errors {
47 let (path, error) = error.display_error();
48 log::error!("Error when replacing {path}: {error}");
49 }
50 let results_output = format_replacement_results(stats.num_successes, None, None);
51
52 Ok(results_output)
53}