worktree-io 0.17.6

A CLI tool to open GitHub issues as git worktree workspaces
Documentation
use anyhow::{Context, Result};
use std::process::Command;

use super::SchemeStatus;

static ICON_PNG: &[u8] = include_bytes!("../../assets/logo.png");

fn desktop_file() -> std::path::PathBuf {
    dirs::data_local_dir()
        .unwrap_or_else(|| std::path::PathBuf::from("~/.local/share"))
        .join("applications")
        .join("worktree-runner.desktop")
}

fn icon_file() -> std::path::PathBuf {
    dirs::data_local_dir()
        .unwrap_or_else(|| std::path::PathBuf::from("~/.local/share"))
        .join("icons/hicolor/256x256/apps/worktree-runner.png")
}

pub fn install() -> Result<()> {
    let exe = std::env::current_exe().context("Failed to get current executable path")?;
    let path = desktop_file();
    let icon = icon_file();
    if let Some(parent) = path.parent() {
        std::fs::create_dir_all(parent)
            .with_context(|| format!("Failed to create {}", parent.display()))?;
    }
    if let Some(parent) = icon.parent() {
        std::fs::create_dir_all(parent)
            .with_context(|| format!("Failed to create {}", parent.display()))?;
    }
    std::fs::write(&icon, ICON_PNG)
        .with_context(|| format!("Failed to write icon to {}", icon.display()))?;
    let content = format!(
        "[Desktop Entry]\nName=Worktree Runner\nExec={exe} open %u\n\
         Type=Application\nNoDisplay=true\nIcon=worktree-runner\n\
         MimeType=x-scheme-handler/worktree;\n",
        exe = exe.display()
    );
    std::fs::write(&path, content)
        .with_context(|| format!("Failed to write desktop file to {}", path.display()))?;
    Command::new("xdg-mime")
        .args([
            "default",
            "worktree-runner.desktop",
            "x-scheme-handler/worktree",
        ])
        .status()
        .context("Failed to run xdg-mime")?;
    println!("Installed desktop entry at {}", path.display());
    Ok(())
}

pub fn uninstall() -> Result<()> {
    let path = desktop_file();
    if path.exists() {
        std::fs::remove_file(&path)
            .with_context(|| format!("Failed to remove {}", path.display()))?;
        println!("Removed {}", path.display());
    } else {
        println!("Not installed — nothing to remove.");
    }
    let icon = icon_file();
    if icon.exists() {
        std::fs::remove_file(&icon)
            .with_context(|| format!("Failed to remove {}", icon.display()))?;
        println!("Removed {}", icon.display());
    }
    Ok(())
}

pub fn status() -> Result<SchemeStatus> {
    let path = desktop_file();
    if path.exists() {
        Ok(SchemeStatus::Installed {
            path: path.display().to_string(),
        })
    } else {
        Ok(SchemeStatus::NotInstalled)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_desktop_file_name() {
        assert!(desktop_file()
            .to_string_lossy()
            .ends_with("worktree-runner.desktop"));
    }
    #[test]
    fn test_icon_file_name() {
        assert!(icon_file()
            .to_string_lossy()
            .ends_with("worktree-runner.png"));
    }
}