dsktp 0.1.0

A utility for listing and interacting with .desktop files on unix systems
Documentation
//! High level functionality of cli tool

mod desktop;
mod support;

use clap::{Parser, Subcommand};
use support::ExecEntry;

pub struct Context {
    pub xdg_data_dirs: String,
}

pub enum SideEffect {
    Exec(String),
    Print(String),
}

pub trait Executable {
    fn execute(&self, context: Context) -> Result<Vec<SideEffect>, &'static str>;
}

#[derive(Parser)]
#[clap(author, version, about, long_about = None)]
#[clap(propagate_version = true)]
pub struct Args {
    #[clap(subcommand)]
    pub command: Command,
}

#[derive(Subcommand)]
pub enum Command {
    /// interact with desktop files by application name
    #[clap(subcommand)]
    App(AppCommand),

    /// interact with desktop files by path
    #[clap(subcommand)]
    Path(PathCommand),
}

impl Executable for Command {
    fn execute(&self, context: Context) -> Result<Vec<SideEffect>, &'static str> {
        match &self {
            Self::App(command) => command.execute(context),
            Self::Path(command) => command.execute(context),
        }
    }
}

#[derive(Subcommand)]
pub enum AppCommand {
    /// List available applications by name
    Ls,

    /// print the exec string for the named application
    How { name: String },

    /// execute the named application
    Exec { name: String },

    /// print the .desktop source for the named application
    Which { name: String },
}

impl Executable for AppCommand {
    fn execute(&self, context: Context) -> Result<Vec<SideEffect>, &'static str> {
        match &self {
            Self::Ls => {
                let exec_entries = support::parse_sorted_data_dirs(&context.xdg_data_dirs);
                let output = exec_entries
                    .into_iter()
                    .map(|ExecEntry { name, .. }| name)
                    .collect::<Vec<String>>()
                    .join("\n");
                let print_se = SideEffect::Print(output);

                Ok(vec![print_se])
            }
            Self::Exec { name } => {
                let maybe_found = support::find_by_name(&context.xdg_data_dirs, name)
                    .map(|ExecEntry { exec, .. }| support::normalize_exec(&exec));

                match maybe_found {
                    Some(exec) => Ok(vec![SideEffect::Exec(exec)]),
                    None => Err("no such application name"),
                }
            }
            Self::How { name } => {
                let maybe_found = support::find_by_name(&context.xdg_data_dirs, name)
                    .map(|ExecEntry { exec, .. }| support::normalize_exec(&exec));

                match maybe_found {
                    Some(exec) => Ok(vec![SideEffect::Print(exec)]),
                    None => Err("no such application name"),
                }
            }
            Self::Which { name } => {
                let maybe_found = support::find_by_name(&context.xdg_data_dirs, name);
                match maybe_found {
                    Some(ExecEntry { path, .. }) => {
                        Ok(vec![SideEffect::Print(support::path_to_string(path))])
                    }
                    None => Err("no such application name"),
                }
            }
        }
    }
}

#[derive(Subcommand)]
pub enum PathCommand {
    /// List available applications by path
    Ls,
}

impl Executable for PathCommand {
    fn execute(&self, context: Context) -> Result<Vec<SideEffect>, &'static str> {
        match &self {
            Self::Ls => {
                let exec_entries = support::parse_sorted_data_dirs(&context.xdg_data_dirs);
                let output = exec_entries
                    .into_iter()
                    .map(|ExecEntry { path, .. }| path)
                    .map(support::path_to_string)
                    .collect::<Vec<String>>()
                    .join("\n");
                let print_se = SideEffect::Print(output);

                Ok(vec![print_se])
            }
        }
    }
}