Skip to main content

oxide_cli/addons/steps/
replace.rs

1use std::path::Path;
2
3use anyhow::{Result, anyhow};
4use inquire::Select;
5
6use crate::addons::manifest::{IfNotFound, ReplaceStep};
7
8use super::{Rollback, render_lines, resolve_target};
9
10pub fn execute_replace(
11  step: &ReplaceStep,
12  project_root: &Path,
13  ctx: &tera::Context,
14) -> Result<Vec<Rollback>> {
15  let paths = resolve_target(&step.target, project_root)?;
16  let replace_lines: Vec<String> = step.replace.lines().map(str::to_string).collect();
17  let rendered_replace = render_lines(&replace_lines, ctx)?.join("\n");
18
19  let mut rollbacks = Vec::new();
20
21  for path in paths {
22    let original = std::fs::read(&path)?;
23    let content = String::from_utf8_lossy(&original).to_string();
24
25    if !content.contains(&step.find) {
26      match step.if_not_found {
27        IfNotFound::Skip => continue,
28        IfNotFound::Error => {
29          return Err(anyhow!(
30            "Pattern {:?} not found in {}",
31            step.find,
32            path.display()
33          ));
34        }
35        IfNotFound::WarnAndAsk => {
36          eprintln!("Warning: {:?} not found in {}", step.find, path.display());
37          let choice =
38            Select::new("How would you like to proceed?", vec!["Continue", "Abort"]).prompt()?;
39          if choice == "Abort" {
40            return Err(anyhow!("Aborted by user"));
41          }
42          continue;
43        }
44      }
45    }
46
47    let new_content = content.replace(&step.find, &rendered_replace);
48    rollbacks.push(Rollback::RestoreFile {
49      path: path.clone(),
50      original,
51    });
52    std::fs::write(&path, new_content)?;
53  }
54
55  Ok(rollbacks)
56}