frep_core/
run.rs

1use std::io::Cursor;
2
3use crate::{
4    line_reader::BufReadExt,
5    replace::replacement_if_match,
6    search::{FileSearcher, ParsedDirConfig, ParsedSearchConfig},
7    validation::{
8        DirConfig, SearchConfig, SimpleErrorHandler, ValidationResult,
9        validate_search_configuration,
10    },
11};
12
13// Perform a find-and-replace recursively in a given directory
14pub fn find_and_replace(
15    search_config: SearchConfig<'_>,
16    dir_config: DirConfig<'_>,
17) -> anyhow::Result<String> {
18    let (parsed_search_config, parsed_dir_config) = parse_config(search_config, Some(dir_config))?;
19    let searcher = FileSearcher::new(
20        parsed_search_config,
21        parsed_dir_config.expect("Found None dir_config when search_type is Files"),
22    );
23    let num_files_replaced = searcher.walk_files_and_replace(None);
24
25    Ok(format!(
26        "Success: {num_files_replaced} file{prefix} updated",
27        prefix = if num_files_replaced != 1 { "s" } else { "" },
28    ))
29}
30
31/// Perform a find-and-replace in a string slice
32pub fn find_and_replace_text(
33    content: &str,
34    search_config: SearchConfig<'_>,
35) -> anyhow::Result<String> {
36    let (parsed_search_config, _) = parse_config(search_config, None)?;
37    let mut result = String::with_capacity(content.len());
38
39    let cursor = Cursor::new(content);
40
41    for line_result in cursor.lines_with_endings() {
42        let (line_bytes, line_ending) = line_result?;
43
44        let line = String::from_utf8(line_bytes)?;
45
46        if let Some(replaced_line) = replacement_if_match(
47            &line,
48            &parsed_search_config.search,
49            &parsed_search_config.replace,
50        ) {
51            result.push_str(&replaced_line);
52        } else {
53            result.push_str(&line);
54        }
55
56        result.push_str(line_ending.as_str());
57    }
58
59    Ok(result)
60}
61
62fn parse_config(
63    search_config: SearchConfig<'_>,
64    dir_config: Option<DirConfig<'_>>,
65) -> anyhow::Result<(ParsedSearchConfig, Option<ParsedDirConfig>)> {
66    let mut error_handler = SimpleErrorHandler::new();
67
68    match validate_search_configuration(search_config, dir_config, &mut error_handler)? {
69        ValidationResult::Success(parsed) => Ok(parsed),
70        ValidationResult::ValidationErrors => Err(anyhow::anyhow!(
71            "{}",
72            error_handler
73                .errors_str()
74                .unwrap_or_else(|| "Unknown validation error".to_string())
75        )),
76    }
77}