frep_core/
run.rs

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                // Channel closed, likely due to early termination
34                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}