Skip to main content

oxide_cli/addons/steps/
inject.rs

1use std::path::Path;
2
3use anyhow::{Result, anyhow};
4use inquire::Select;
5
6use crate::addons::manifest::{IfNotFound, InjectStep};
7
8use super::{Rollback, render_lines, resolve_target};
9
10pub fn execute_inject(
11  step: &InjectStep,
12  project_root: &Path,
13  ctx: &tera::Context,
14) -> Result<Vec<Rollback>> {
15  let paths = resolve_target(&step.target, project_root)?;
16  let lines: Vec<String> = step.content.lines().map(str::to_string).collect();
17  let rendered = render_lines(&lines, ctx)?;
18
19  let mut rollbacks = Vec::new();
20
21  for path in paths {
22    let original = std::fs::read(&path)?;
23    let mut file_lines: Vec<String> = String::from_utf8_lossy(&original)
24      .lines()
25      .map(str::to_string)
26      .collect();
27
28    let marker = step.after.as_deref().or(step.before.as_deref());
29
30    if let Some(marker) = marker {
31      match file_lines.iter().position(|l| l.contains(marker)) {
32        Some(idx) => {
33          let insert_idx = if step.after.is_some() { idx + 1 } else { idx };
34          for (i, line) in rendered.iter().enumerate() {
35            file_lines.insert(insert_idx + i, line.clone());
36          }
37        }
38        None => match step.if_not_found {
39          IfNotFound::Skip => continue,
40          IfNotFound::Error => {
41            return Err(anyhow!(
42              "Marker {:?} not found in {}",
43              marker,
44              path.display()
45            ));
46          }
47          IfNotFound::WarnAndAsk => {
48            eprintln!(
49              "Warning: marker {:?} not found in {}",
50              marker,
51              path.display()
52            );
53            let choice =
54              Select::new("How would you like to proceed?", vec!["Continue", "Abort"]).prompt()?;
55            if choice == "Abort" {
56              return Err(anyhow!("Aborted by user"));
57            }
58            continue;
59          }
60        },
61      }
62    } else {
63      let mut new_lines = rendered.clone();
64      new_lines.extend(file_lines);
65      file_lines = new_lines;
66    }
67
68    rollbacks.push(Rollback::RestoreFile {
69      path: path.clone(),
70      original,
71    });
72    std::fs::write(&path, file_lines.join("\n"))?;
73  }
74
75  Ok(rollbacks)
76}