1use crate::exceptions::AicoError;
2use crate::session::Session;
3use std::env;
4use std::fs;
5use std::io::IsTerminal;
6use std::io::{Read, Write};
7use std::process::Command;
8
9fn run_editor(content: &str) -> Result<String, AicoError> {
10 let mut temp = tempfile::Builder::new().suffix(".md").tempfile()?;
12 temp.write_all(content.as_bytes())?;
13 let temp_path = temp.path().to_path_buf();
14
15 let editor = env::var("EDITOR").unwrap_or_else(|_| "vi".into());
17 let parts = shlex::split(&editor).ok_or_else(|| {
18 AicoError::Configuration(format!("Failed to parse EDITOR variable: '{}'", editor))
19 })?;
20
21 if parts.is_empty() {
22 return Err(AicoError::Configuration(
23 "EDITOR environment variable is empty".into(),
24 ));
25 }
26
27 let status = Command::new(&parts[0])
28 .args(&parts[1..])
29 .arg(&temp_path)
30 .status()
31 .map_err(|e| {
32 if e.kind() == std::io::ErrorKind::NotFound {
33 AicoError::InvalidInput(format!(
34 "Editor '{}' not found. Please set $EDITOR.",
35 parts[0]
36 ))
37 } else {
38 AicoError::Io(e)
39 }
40 })?;
41
42 if !status.success() {
43 return Err(AicoError::InvalidInput(format!(
44 "Editor closed with exit code {}. Aborting.",
45 status.code().unwrap_or(1)
46 )));
47 }
48
49 let mut buffer = String::new();
51 fs::File::open(&temp_path)?.read_to_string(&mut buffer)?;
52 Ok(buffer)
53}
54
55pub fn run(index_str: String, prompt_flag: bool) -> Result<(), AicoError> {
56 let mut session = Session::load_active()?;
57 let pair_idx = session.resolve_pair_index(&index_str)?;
58
59 let msg_idx = if prompt_flag {
61 pair_idx * 2
62 } else {
63 pair_idx * 2 + 1
64 };
65
66 let global_idx = session.view.message_indices[msg_idx];
67 let records = session.store.read_many(&[global_idx])?;
68 let record = records
69 .first()
70 .ok_or_else(|| AicoError::SessionIntegrity("Record not found".into()))?;
71
72 let original_content = &record.content;
73
74 let is_piped = !std::io::stdin().is_terminal();
75 let force_editor = std::env::var("AICO_FORCE_EDITOR").is_ok();
76
77 let new_content = if is_piped && !force_editor {
78 let mut buffer = String::new();
79 std::io::stdin().read_to_string(&mut buffer)?;
80 buffer
81 } else {
82 run_editor(original_content)?
83 };
84
85 let norm_new = new_content.replace("\r\n", "\n");
87 let norm_old = original_content.replace("\r\n", "\n");
88
89 if norm_new.trim().is_empty() || norm_new == norm_old {
90 println!("No changes detected. Aborting.");
91 return Ok(());
92 }
93
94 session.edit_message(msg_idx, norm_new)?;
96
97 let target = if prompt_flag { "prompt" } else { "response" };
98 println!("Updated {} for message pair {}.", target, pair_idx);
99
100 Ok(())
101}