quick-links-rofi 0.1.2

A rust program to launch a rofi menu with a bunch of links
Documentation
use crate::config::Config;
use crate::config::I3Switcher;
use crate::config::WorkspaceSwitcher;
use crate::input::Items;
use anyhow::anyhow;
use std::io::Write;
use std::process::Command;
use std::process::Output;
use std::process::Stdio;

pub trait ExecSwitcher {
    fn exec(&self) -> Option<Result<Output, std::io::Error>>;
}

impl ExecSwitcher for WorkspaceSwitcher {
    fn exec(&self) -> Option<Result<Output, std::io::Error>> {
        if let Some(command) = &self.custom {
            command.exec()
        } else if let Some(i3_swicth) = &self.i3 {
            i3_swicth.exec()
        } else {
            None
        }
    }
}

impl ExecSwitcher for String {
    fn exec(&self) -> Option<Result<Output, std::io::Error>> {
        let values: Vec<String> = self.split(" ").map(|s| s.to_string()).collect();
        let comm = Command::new(values.first()?)
            .args(values[1..].iter())
            .output();
        Some(comm)
    }
}

impl ExecSwitcher for I3Switcher {
    fn exec(&self) -> Option<Result<Output, std::io::Error>> {
        Some(
            Command::new("i3-msg")
                .args([
                    "workspace",
                    "number",
                    self.workspace_number.to_string().as_str(),
                ])
                .output(),
        )
    }
}

pub fn launch_rofi(items: &Items, config: &Config) -> anyhow::Result<Option<String>> {
    let names = items.get_names();

    let mut child = Command::new("rofi")
        .args(["-dmenu", "-i", "-theme", config.theme.as_str()])
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .spawn()?;

    let mut stdin = child.stdin.take().ok_or(anyhow!("Cannot open stdin"))?;
    std::thread::spawn(move || {
        stdin
            .write_all(names.as_bytes())
            .expect("Failed to write to stdin");
    });
    let output = child.wait_with_output()?;

    if output.status.success() {
        let output_string = std::str::from_utf8(&output.stdout)?;
        if output_string.trim().is_empty() {
            Err(anyhow!("Empty selection"))
        } else {
            Ok(Some(String::from(output_string)))
        }
    } else {
        Ok(None)
    }
}

pub fn launch_link(s: &str, items: &Items, config: &Config) -> anyhow::Result<()> {
    if let Some(switcher) = &config.workspace_switcher {
        if let Some(output) = switcher.exec() {
            if !output?.status.success() {
                return Err(anyhow!("i3-msg failed"));
            }
        }
    }

    let link = items.get_link(s.trim()).ok_or(anyhow!("Invalid item"))?;
    Command::new(config.browser_command_name.as_str())
        .args([link])
        .output()?;

    Ok(())
}