use crate::cli::EditArgs;
use crate::error::{Error, Result};
use crate::sops::{self, FileType};
use crate::ui::Ui;
const DEFAULT_EDITOR: &str = "vi";
pub fn run(ui: &Ui, args: &EditArgs) -> Result<()> {
ui.header("sopsy edit");
sops::ensure_available()?;
if !args.file.exists() {
return Err(Error::FileNotFound(args.file.clone()));
}
let editor = resolve_editor(args.editor.as_deref());
ui.debug(format!("using editor `{editor}`"));
let file_type = FileType::from_path(&args.file);
let ty = file_type.as_sops_type();
let mut sops_args = vec![
"--input-type".to_string(),
ty.to_string(),
"--output-type".to_string(),
ty.to_string(),
];
sops_args.extend(args.sops_args.iter().cloned());
ui.debug(format!("detected file type `{ty}`"));
ui.flush();
sops::edit(&args.file, Some(&editor), &sops_args).map_err(|err| match err {
Error::ProcessFailed { code, message, .. } => Error::ProcessFailed {
tool: "sops".to_string(),
code,
message: format!(
"failed to edit {} (is it a valid sops-encrypted file?): {message}",
args.file.display()
),
},
other => other,
})?;
ui.success(format!("saved changes to {}", args.file.display()));
Ok(())
}
fn resolve_editor(flag: Option<&str>) -> String {
if let Some(editor) = flag {
return editor.to_string();
}
if let Ok(editor) = std::env::var("EDITOR")
&& !editor.trim().is_empty()
{
return editor;
}
if let Ok(visual) = std::env::var("VISUAL")
&& !visual.trim().is_empty()
{
return visual;
}
DEFAULT_EDITOR.to_string()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn editor_flag_takes_precedence() {
assert_eq!(resolve_editor(Some("nano")), "nano");
}
#[test]
fn editor_falls_back_to_default_without_flag_or_env() {
assert_eq!(DEFAULT_EDITOR, "vi");
assert_eq!(resolve_editor(Some("vim")), "vim");
}
}