term-detect 0.1.8

Terminal emulator detector
Documentation
use crate::{DetectionError, Terminal};
use configparser::ini::Ini;
use std::{env::var, fs::read_to_string, path::Path};

// Open a .desktop file and read the Exec section
fn xdg_term_from_desktop_file<P: AsRef<Path>>(path: P) -> Result<Terminal, DetectionError> {
    let path = path.as_ref();
    let mut config = Ini::new_cs();
    if let Err(e) = config.load(path) {
        return Err(DetectionError::IniParseError(e));
    };
    let Some(term) = config.get("Desktop Entry", "Exec") else {
        return Err(DetectionError::DesktopFileError(
            path.to_string_lossy().to_string(),
        ));
    };
    Ok(term.into())
}

// Try to find the .desktop file and pass it to xdg_term_from_desktop_file
fn xdg_try_desktop<P: AsRef<Path>>(path: P) -> Result<Terminal, DetectionError> {
    let path = path.as_ref();
    let mut errors = vec![];
    macro_rules! return_or_add {
        ($($arg:tt)*) => {
            match xdg_term_from_desktop_file(format!($($arg)*)) {
                Ok(term) => return Ok(term),
                Err(e) => errors.push(e),
            }
        };
    }

    if path.is_absolute() {
        return xdg_term_from_desktop_file(path);
    }
    if let Ok(home) = var("HOME") {
        return_or_add!(
            "{}/.local/share/applications/{}",
            home,
            path.to_string_lossy()
        );
    };
    return_or_add!("/usr/share/applications/{}", path.to_string_lossy());

    Err(errors.into())
}

// Try out every .desktop file listed in the config
fn xdg_try_config<P: AsRef<Path>>(path: P) -> Result<Terminal, DetectionError> {
    let path = path.as_ref();
    let config = read_to_string(path)?;
    let lines = config.lines();
    let mut errors = vec![];

    for maybe_term_desktop in lines {
        match xdg_try_desktop(maybe_term_desktop) {
            Ok(term) => return Ok(term),
            Err(e) => errors.push(e),
        }
    }

    Err(errors.into())
}

/// Try to detect the terminal with the proposed XDG spec
///
/// <https://github.com/Vladimir-csp/xdg-terminal-exec>
pub fn detect_terminal_xdg_proposed() -> Result<Terminal, DetectionError> {
    let home = var("HOME");
    let desktop = var("XDG_CURRENT_DESKTOP");
    let mut errors = vec![];
    macro_rules! return_or_add {
        ($($arg:tt)*) => {
            match xdg_try_config(format!($($arg)*)) {
                Ok(term) => return Ok(term),
                Err(e) => errors.push(e),
            }
        };
    }

    if let Ok(home) = home {
        if let Ok(desktop) = &desktop {
            return_or_add!(
                "{}/.config/{}-xdg-terminals.list",
                home,
                desktop.to_lowercase()
            );
        }
        return_or_add!("{}/.config/xdg-terminals.list", home);
    };
    if let Ok(desktop) = &desktop {
        return_or_add!("/etc/xdg/{}-xdg-terminals.list", desktop.to_lowercase());
    };
    return_or_add!("/etc/xdg/xdg-terminals.list");
    if let Ok(desktop) = desktop {
        return_or_add!("/usr/etc/xdg/{}-xdg-terminals.list", desktop.to_lowercase());
    };
    return_or_add!("/usr/etc/xdg/xdg-terminals.list");

    Err(errors.into())
}