oxide_cli/addons/steps/
replace.rs1use std::path::Path;
2
3use anyhow::{anyhow, Result};
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!("Pattern {:?} not found in {}", step.find, path.display()));
30 }
31 IfNotFound::WarnAndAsk => {
32 eprintln!("Warning: {:?} not found in {}", step.find, path.display());
33 let choice =
34 Select::new("How would you like to proceed?", vec!["Continue", "Abort"]).prompt()?;
35 if choice == "Abort" {
36 return Err(anyhow!("Aborted by user"));
37 }
38 continue;
39 }
40 }
41 }
42
43 let new_content = content.replace(&step.find, &rendered_replace);
44 rollbacks.push(Rollback::RestoreFile { path: path.clone(), original });
45 std::fs::write(&path, new_content)?;
46 }
47
48 Ok(rollbacks)
49}