1use std::fs::File;
2use std::io;
3use std::path::PathBuf;
4use std::process::Command;
5
6use ropey::Rope;
7
8use crate::diff::Diff;
9use crate::edit::FlakeEdit;
10use crate::error::FlakeEditError;
11use crate::validate;
12
13use super::state::AppState;
14
15#[derive(Debug, Default)]
17pub struct FlakeBuf {
18 text: Rope,
19 path: PathBuf,
20}
21
22impl FlakeBuf {
23 pub fn from_path(path: PathBuf) -> io::Result<Self> {
24 let text = Rope::from_reader(&mut io::BufReader::new(File::open(&path)?))?;
25 Ok(Self { text, path })
26 }
27
28 pub fn text(&self) -> &Rope {
29 &self.text
30 }
31
32 pub fn path(&self) -> &PathBuf {
33 &self.path
34 }
35
36 pub fn write(&self, content: &str) -> io::Result<()> {
37 std::fs::write(&self.path, content)
38 }
39}
40
41#[derive(Debug)]
45pub struct Editor {
46 flake: FlakeBuf,
47}
48
49impl Editor {
50 pub fn new(flake: FlakeBuf) -> Self {
51 Self { flake }
52 }
53
54 pub fn from_path(path: PathBuf) -> io::Result<Self> {
55 let flake = FlakeBuf::from_path(path)?;
56 Ok(Self { flake })
57 }
58
59 pub fn text(&self) -> String {
60 self.flake.text().to_string()
61 }
62
63 pub fn path(&self) -> &PathBuf {
64 self.flake.path()
65 }
66
67 pub fn create_flake_edit(&self) -> Result<FlakeEdit, FlakeEditError> {
68 FlakeEdit::from_text(&self.text())
69 }
70
71 fn run_nix_flake_lock(&self) -> io::Result<()> {
72 let flake_dir = match self.flake.path.parent() {
73 Some(parent) if !parent.as_os_str().is_empty() => parent.to_path_buf(),
74 _ => PathBuf::from("."),
75 };
76
77 let output = Command::new("nix")
78 .args(["flake", "lock"])
79 .current_dir(&flake_dir)
80 .output()?;
81
82 if !output.status.success() {
83 let stderr = String::from_utf8_lossy(&output.stderr);
84 eprintln!("Warning: nix flake lock failed: {}", stderr);
85 return Err(io::Error::other(format!(
86 "nix flake lock failed: {}",
87 stderr
88 )));
89 }
90
91 println!("Updated flake.lock");
92 Ok(())
93 }
94
95 pub fn apply_or_diff(&self, new_content: &str, state: &AppState) -> Result<(), FlakeEditError> {
99 let validation = validate::validate(new_content);
100 if validation.has_errors() {
101 return Err(FlakeEditError::Validation(validation.errors));
102 }
103
104 if state.diff {
105 let old = self.text();
106 let diff = Diff::new(&old, new_content);
107 diff.compare();
108 } else {
109 self.flake.write(new_content)?;
110
111 if !state.no_lock
112 && let Err(e) = self.run_nix_flake_lock()
113 {
114 eprintln!("Warning: Failed to update lockfile: {}", e);
115 }
116 }
117 Ok(())
118 }
119}