kr580 1.0.0

Desktop KR580VM80 / Intel 8080 emulator.
Documentation
use std::path::{Path, PathBuf};

pub fn register() -> Result<(), String> {
    let kr = std::env::current_exe().map_err(|e| format!("current_exe: {e}"))?;
    register_for_executable(&kr, crate::install_mode::InstallScope::User)
}

pub fn register_for_executable(
    exe: &Path,
    _scope: crate::install_mode::InstallScope,
) -> Result<(), String> {
    let exe_str = exe
        .to_str()
        .ok_or_else(|| "executable path is not valid UTF-8".to_owned())?;
    let mime_dir = mime_dir();
    let apps_dir = apps_dir();
    let hicolor_dir = hicolor_icon_dir();

    std::fs::create_dir_all(&mime_dir).map_err(|e| format!("create mime dir: {e}"))?;
    std::fs::create_dir_all(&apps_dir).map_err(|e| format!("create apps dir: {e}"))?;
    std::fs::create_dir_all(&hicolor_dir).map_err(|e| format!("create icons dir: {e}"))?;

    let mime_file = mime_dir.join("application-x-kr580.xml");
    let desktop_file = apps_dir.join("kr580.desktop");
    let dest_icon = hicolor_dir.join("kr580.png");

    std::fs::write(&mime_file, mime_xml()).map_err(|e| format!("write mime file: {e}"))?;
    std::fs::write(&desktop_file, desktop_entry(exe_str))
        .map_err(|e| format!("write desktop file: {e}"))?;
    if let Some(icon) = super::find_icon() {
        std::fs::copy(&icon, &dest_icon).map_err(|e| format!("copy icon: {e}"))?;
    }

    update_databases();
    Ok(())
}

pub fn unregister() -> Result<(), String> {
    let _ = std::fs::remove_file(mime_dir().join("application-x-kr580.xml"));
    let _ = std::fs::remove_file(apps_dir().join("kr580.desktop"));
    let _ = std::fs::remove_file(hicolor_icon_dir().join("kr580.png"));
    update_databases();
    Ok(())
}

pub fn unregister_for_executable(
    exe: &Path,
    _scope: crate::install_mode::InstallScope,
) -> Result<(), String> {
    let exe_str = exe
        .to_str()
        .ok_or_else(|| "executable path is not valid UTF-8".to_owned())?;
    let desktop_file = apps_dir().join("kr580.desktop");
    let current = std::fs::read_to_string(&desktop_file).unwrap_or_default();
    if current.contains(&format!("Exec={exe_str} %f")) {
        unregister()?;
    }
    Ok(())
}

pub fn is_registered() -> bool {
    mime_dir().join("application-x-kr580.xml").is_file()
        && apps_dir().join("kr580.desktop").is_file()
}

fn home_dir() -> PathBuf {
    std::env::var("HOME")
        .map(PathBuf::from)
        .unwrap_or_else(|_| PathBuf::from("."))
}

fn mime_dir() -> PathBuf {
    home_dir().join(".local/share/mime/packages")
}

fn apps_dir() -> PathBuf {
    home_dir().join(".local/share/applications")
}

fn hicolor_icon_dir() -> PathBuf {
    home_dir().join(".local/share/icons/hicolor/64x64/apps")
}

fn mime_xml() -> &'static str {
    r#"<?xml version="1.0" encoding="UTF-8"?>
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
  <mime-type type="application/x-kr580">
    <comment>KR580 snapshot</comment>
    <glob pattern="*.580"/>
  </mime-type>
</mime-info>
"#
}

fn desktop_entry(exec: &str) -> String {
    format!(
        "[Desktop Entry]\n\
         Name=KR580 Emulator\n\
         Comment=KR580 emulator\n\
         Exec={} %f\n\
         Icon=kr580\n\
         Type=Application\n\
         Terminal=false\n\
         MimeType=application/x-kr580;\n\
         Categories=Development;\n",
        exec
    )
}

fn update_databases() {
    let _ = std::process::Command::new("update-mime-database")
        .arg(home_dir().join(".local/share/mime"))
        .status();
    let _ = std::process::Command::new("update-desktop-database")
        .arg(apps_dir())
        .status();
}