gdritter repos dmesktop / master src / main.rs
master

Tree @master (Download .tar.gz)

main.rs @master

9baba44
a5a0730
9baba44
 
 
 
 
 
7213fad
9baba44
 
7213fad
9baba44
7213fad
9baba44
7213fad
9baba44
 
 
7213fad
9baba44
 
7213fad
9baba44
 
7213fad
537aa2e
9baba44
 
 
 
7213fad
9baba44
 
 
 
 
 
7213fad
9baba44
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7213fad
9baba44
a5a0730
 
 
9baba44
 
 
 
 
 
 
 
7213fad
 
 
 
 
 
 
9baba44
7213fad
9baba44
 
 
 
 
 
 
 
 
 
 
 
#[macro_use] extern crate failure;
extern crate xdg;
extern crate xdg_desktop;

use failure::Error;
use xdg_desktop::{DesktopEntry, EntryType};

use std::io::{Write};
use std::process::{self,Command,Stdio};
use std::os::unix::process::CommandExt;

fn ensure_rofi() -> Result<(), Error> {
    let _ = Command::new("which")
        .args(&["rofi"])
        .output()
        .map_err(|_| format_err!("could not find `rofi'"))?;
    Ok(())
}

/// Given a list of strings, we provide them to rofi and return back
/// the one which the user chose (or an empty string, if the user
/// chose nothing)
fn rofi_choose<'a, I>(choices: I) -> Result<String, Error>
    where I: Iterator<Item=&'a String>
{
    let mut rofi = Command::new("rofi")
        .args(&["-dmenu", "-i", "-l", "10"])
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .spawn()?;
    {
        let stdin = rofi.stdin.as_mut().unwrap();
        for c in choices.into_iter() {
            stdin.write(c.as_bytes())?;
            stdin.write(b"\n")?;
        }
    }

    let output = rofi.wait_with_output()?;
    Ok(String::from_utf8(output.stdout)?.trim().to_owned())
}

fn is_not_metavar(s: &&str) -> bool {
    !(s.starts_with("%") && s.len() == 2)
}

fn run_command(cmd: &Option<String>) -> Result<(), Error> {
    if let &Some(ref cmd) = cmd {
        let mut iter = cmd.split_whitespace();
        process::Command::new(iter.next().unwrap())
            .args(iter.filter(is_not_metavar))
            .exec();
    } else {
        Err(format_err!("No command specified in file!"))?;
    }
    Ok(())
}

fn fetch_entries() -> Result<Vec<xdg_desktop::DesktopEntry>, Error> {
    let mut entries = Vec::new();
    for f in xdg::BaseDirectories::new()?.list_data_files("applications") {
        if f.extension().map_or(false, |e| e == "desktop") {
            let mut f = std::fs::File::open(f)?;
            match xdg_desktop::DesktopEntry::from_file(&mut f) {
                Ok(e) => if e.is_application() {
                    entries.push(e);
                },
                _ => (),
            }
        }
    }
    Ok(entries)
}

fn main() -> Result<(), Error> {
    ensure_rofi()?;

    let entries = fetch_entries()?;

    let choice = rofi_choose(entries.iter()
                              .map(|e| &e.info.name))?;
    println!("Choice is {}", choice);

    match entries.iter().find(|e| e.info.name == choice) {
        Some(&DesktopEntry {
            typ: EntryType::Application(ref app),
            info: _,
        }) => run_command(&app.exec)?,
        _ => (),
    }
    Ok(())
}