summaryrefslogtreecommitdiffstats
path: root/src/shell_menu.rs
blob: 36d17ba695e7a5baf87da4bf639a694854c0037e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
use anyhow::{Context, Result};

use crate::impl_selectable_content;
use crate::log_line;
use crate::opener::{execute_in_child_without_output, execute_in_child_without_output_with_path};
use crate::status::Status;
use crate::utils::is_program_in_path;

#[derive(Clone)]
pub struct ShellMenu {
    pub content: Vec<(String, bool)>,
    index: usize,
}

impl Default for ShellMenu {
    fn default() -> Self {
        let index = 0;
        let content = vec![("shell".to_owned(), false)];
        Self { content, index }
    }
}

impl ShellMenu {
    pub fn new(path: &str) -> Result<Self> {
        let mut shell_menu = Self::default();
        let file =
            std::fs::File::open(std::path::Path::new(&shellexpand::tilde(path).to_string()))?;
        let yaml = serde_yaml::from_reader(file)?;
        shell_menu.update_from_file(&yaml)?;
        Ok(shell_menu)
    }

    fn update_from_file(&mut self, yaml: &serde_yaml::mapping::Mapping) -> Result<()> {
        for (key, mapping) in yaml.into_iter() {
            let Some(command) = key.as_str() else {
                continue;
            };
            if !is_program_in_path(command) {
                continue;
            }
            let command = command.to_owned();
            let Some(require_cwd) = mapping.get("cwd") else {
                continue;
            };
            let Some(require_cwd) = require_cwd.as_bool() else {
                continue;
            };
            self.content.push((command, require_cwd));
        }
        Ok(())
    }

    pub fn execute(&self, status: &Status) -> Result<()> {
        let (name, require_cwd) = &self.content[self.index];
        if name.as_str() == "shell" {
            Self::require_cwd(status)?
        } else if *require_cwd {
            Self::require_cwd_and_command(status, name.as_str())?
        } else {
            Self::simple(status, name.as_str())?
        };
        log_line!("Executed {name}");
        Ok(())
    }

    fn require_cwd_and_command(status: &Status, command: &str) -> Result<()> {
        let tab = status.selected_non_mut();
        let path = tab
            .directory_of_selected()?
            .to_str()
            .context("event_shell: couldn't parse the directory")?;
        execute_in_child_without_output(&status.opener.terminal, &["-d", path, "-e", command])?;
        Ok(())
    }

    fn simple(status: &Status, command: &str) -> Result<()> {
        execute_in_child_without_output(&status.opener.terminal, &["-e", command])?;
        Ok(())
    }

    fn _simple_with_args(status: &Status, args: Vec<&str>) -> Result<()> {
        execute_in_child_without_output(&status.opener.terminal, &args)?;
        Ok(())
    }

    fn require_cwd(status: &Status) -> Result<()> {
        let tab = status.selected_non_mut();
        let path = tab.directory_of_selected()?;
        execute_in_child_without_output_with_path(&status.opener.terminal, path, None)?;
        Ok(())
    }
}

type SBool = (String, bool);

impl_selectable_content!(SBool, ShellMenu);