use std::path::Path;
use std::process::Command;
use anyhow::{bail, Context, Result};
use appimg_core::desktop_entry::{self, DesktopEntry};
use appimg_core::{caches, list, Paths};
use crate::cli::EditArgs;
use crate::ui::Ui;
use crate::Outcome;
const MANAGED_KEYS: &[&str] = &[
desktop_entry::KEY_MANAGED,
desktop_entry::KEY_SLUG,
desktop_entry::KEY_SOURCE,
desktop_entry::KEY_VERSION,
desktop_entry::KEY_UPDATE_INFO,
desktop_entry::KEY_INSTALLED_AT,
];
pub struct Edited {
pub changed: bool,
pub warnings: Vec<String>,
}
pub fn run(paths: &Paths, ui: &Ui, args: &EditArgs) -> Result<Outcome> {
if !ui.is_interactive() {
bail!("editing needs a terminal");
}
let app = list::find(paths, &args.name)?;
let edited = edit_entry(paths, &app.desktop_entry_path)?;
if !edited.changed {
ui.info("Nothing changed.");
return Ok(Outcome::NothingToDo);
}
for warning in &edited.warnings {
ui.warn(warning);
}
ui.info(&format!("Updated {}.", app.desktop_entry_path.display()));
Ok(Outcome::Done)
}
pub fn edit_entry(paths: &Paths, entry_path: &Path) -> Result<Edited> {
let original = DesktopEntry::read(entry_path)?;
let mut edited = edit_in_editor(&original)?;
if edited == original {
return Ok(Edited { changed: false, warnings: Vec::new() });
}
for key in MANAGED_KEYS {
if edited.get(key).is_none() {
if let Some(value) = original.get(key) {
edited.set(*key, value);
}
}
}
desktop_entry::validate_categories(&edited.categories())?;
edited.write(entry_path)?;
let warnings = caches::validate_desktop_entry(entry_path);
caches::refresh(paths);
Ok(Edited { changed: true, warnings })
}
fn edit_in_editor(entry: &DesktopEntry) -> Result<DesktopEntry> {
let editor = std::env::var("EDITOR")
.or_else(|_| std::env::var("VISUAL"))
.unwrap_or_else(|_| "vi".to_string());
let dir = tempfile::Builder::new().prefix("appimg-edit-").tempdir()?;
let file = dir.path().join("entry.desktop");
std::fs::write(&file, entry.to_string())?;
let status = Command::new(&editor)
.arg(&file)
.status()
.with_context(|| format!("cannot start the editor {editor:?}"))?;
if !status.success() {
bail!("the editor {editor:?} exited with {status}");
}
let text = std::fs::read_to_string(&file)?;
Ok(DesktopEntry::parse(&text))
}