main.rs 2.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. #[macro_use] extern crate failure;
  2. extern crate xdg;
  3. extern crate xdg_desktop;
  4. use failure::Error;
  5. use xdg_desktop::{DesktopEntry, EntryType};
  6. use std::io::{Write};
  7. use std::process::{self,Command,Stdio};
  8. use std::os::unix::process::CommandExt;
  9. fn ensure_rofi() -> Result<(), Error> {
  10. let _ = Command::new("which")
  11. .args(&["rofi"])
  12. .output()
  13. .map_err(|_| format_err!("could not find `rofi'"))?;
  14. Ok(())
  15. }
  16. /// Given a list of strings, we provide them to rofi and return back
  17. /// the one which the user chose (or an empty string, if the user
  18. /// chose nothing)
  19. fn rofi_choose<'a, I>(choices: I) -> Result<String, Error>
  20. where I: Iterator<Item=&'a String>
  21. {
  22. let mut rofi = Command::new("rofi")
  23. .args(&["-dmenu", "-i", "-l", "10"])
  24. .stdin(Stdio::piped())
  25. .stdout(Stdio::piped())
  26. .spawn()?;
  27. {
  28. let stdin = rofi.stdin.as_mut().unwrap();
  29. for c in choices.into_iter() {
  30. stdin.write(c.as_bytes())?;
  31. stdin.write(b"\n")?;
  32. }
  33. }
  34. let output = rofi.wait_with_output()?;
  35. Ok(String::from_utf8(output.stdout)?.trim().to_owned())
  36. }
  37. fn is_not_metavar(s: &&str) -> bool {
  38. !(s.starts_with("%") && s.len() == 2)
  39. }
  40. fn run_command(cmd: &Option<String>) -> Result<(), Error> {
  41. if let &Some(ref cmd) = cmd {
  42. let mut iter = cmd.split_whitespace();
  43. process::Command::new(iter.next().unwrap())
  44. .args(iter.filter(is_not_metavar))
  45. .exec();
  46. } else {
  47. Err(format_err!("No command specified in file!"))?;
  48. }
  49. Ok(())
  50. }
  51. fn fetch_entries() -> Result<Vec<xdg_desktop::DesktopEntry>, Error> {
  52. let mut entries = Vec::new();
  53. for f in xdg::BaseDirectories::new()?.list_data_files("applications") {
  54. if f.extension().map_or(false, |e| e == "desktop") {
  55. let mut f = std::fs::File::open(f)?;
  56. match xdg_desktop::DesktopEntry::from_file(&mut f) {
  57. Ok(e) => if e.is_application() {
  58. entries.push(e);
  59. },
  60. _ => (),
  61. }
  62. }
  63. }
  64. Ok(entries)
  65. }
  66. fn main() -> Result<(), Error> {
  67. ensure_rofi()?;
  68. let entries = fetch_entries()?;
  69. let choice = rofi_choose(entries.iter()
  70. .map(|e| &e.info.name))?;
  71. println!("Choice is {}", choice);
  72. match entries.iter().find(|e| e.info.name == choice) {
  73. Some(&DesktopEntry {
  74. typ: EntryType::Application(ref app),
  75. info: _,
  76. }) => run_command(&app.exec)?,
  77. _ => (),
  78. }
  79. Ok(())
  80. }