hyprshell_config_lib/
explain.rs

1use crate::Config;
2use std::fmt::Write;
3use std::path::Path;
4
5const BOLD: &str = "\x1b[1m";
6const ITALIC: &str = "\x1b[3m";
7const BLUE: &str = "\x1b[34m";
8const GREEN: &str = "\x1b[32m";
9const RESET: &str = "\x1b[0m";
10
11#[must_use]
12pub fn explain(
13    config: &Config,
14    config_path: &Path,
15    enable_color: bool,
16    enable_header: bool,
17) -> String {
18    let (bold, italic, blue, green, reset) = if enable_color {
19        (BOLD, ITALIC, BLUE, GREEN, RESET)
20    } else {
21        ("", "", "", "", "")
22    };
23
24    let config_path_display = config_path.display();
25    let mut builder = if enable_header {
26        format!(
27            "{bold}{green}Config is valid{reset} ({config_path_display})\n{bold}Explanation{reset} ({blue}blue{reset} are keys, {bold}{blue}bold blue{reset} keys can be configured in config):{reset}\n",
28        )
29    } else {
30        String::new()
31    };
32
33    if let Some(windows) = &config.windows {
34        if let Some(overview) = &windows.overview {
35            let _ = builder.write_str(&format!(
36                "Use {bold}{blue}{}{reset} + {bold}{blue}{}{reset} to open the Overview. Use {blue}tab{reset} and {blue}grave{reset} / {blue}shift{reset} + {blue}tab{reset} to select a different window, press {blue}return{reset} to switch\n\
37                You can also use the {blue}arrow keys{reset} or {bold}{blue}{}{reset} + vim keys to navigate the workspaces. Use {blue}Esc{reset} to close the overview.\n",
38                overview.modifier,
39                overview.key,
40                overview.launcher.launch_modifier
41            ));
42            let _ = builder.write_str(&format!(
43                "After opening the Overview the {bold}Launcher{reset} is available:\n"
44            ));
45            let mut any_plugin = false;
46            if let Some(_applications) = overview.launcher.plugins.applications.as_ref() {
47                any_plugin = true;
48                let _ = builder.write_str(&format!("\t- Start typing to search through applications (sorted by how often they were opened). Press {blue}return{reset} to launch the first app, use {blue}Ctrl{reset} + {blue}1{reset}/{blue}2{reset}/{blue}3{reset}/... to open the second, third, etc.\n"));
49            }
50            if overview.launcher.plugins.terminal.is_some() {
51                any_plugin = true;
52                let _ = builder.write_str(&format!(
53                    "\t- Press {blue}Ctrl{reset} + {blue}t{reset} to run the typed command in a terminal.\n"
54                ));
55            }
56            if overview.launcher.plugins.shell.is_some() {
57                any_plugin = true;
58                let _ = builder.write_str(&format!(
59                    "\t- Press {blue}Ctrl{reset} + {blue}r{reset} to run the typed command in the background.\n",
60                ));
61            }
62            if let Some(engines) = &overview.launcher.plugins.websearch {
63                any_plugin = true;
64                let _ =    builder.write_str(&format!("\t- Press {blue}Ctrl{reset} + {bold}{blue}<key>{reset} to search the typed text in any of the configured SearchEngines: {}.\n",
65                                                      engines.engines.iter().map(|e| e.name.to_string()).collect::<Vec<_>>().join(", ")));
66            }
67            if overview.launcher.plugins.calc.is_some() {
68                any_plugin = true;
69                let _ =   builder.write_str(
70                    "\t- Typing a mathematical expression will calculate it and display the result in the launcher.\n",
71                );
72            }
73            if overview.launcher.plugins.path.is_some() {
74                any_plugin = true;
75                let _ = builder.write_str(
76                    "\t- Paths (starting with ~ or /) can be open in default file-manager.\n",
77                );
78            }
79            if overview.launcher.plugins.actions.is_some() {
80                any_plugin = true;
81                let _ = builder.write_str(
82                    "\t- Type Reboot/Shutdown/etc. to run corresponding commands. Type `actions` to see all available ones.\n",
83                );
84            }
85            if !any_plugin {
86                let _ = builder.write_str(&format!(
87                    "{italic}\t<No plugins enabled in launcher>{reset}\n"
88                ));
89            }
90        } else {
91            let _ = builder.write_str(&format!("{italic}<Overview disabled>{reset}\n"));
92        }
93        builder.push('\n');
94
95        if let Some(switch) = &windows.switch {
96            let _ = builder.write_str(&format!(
97                "Press {bold}{blue}{}{reset} + {blue}tab{reset} and hold {bold}{blue}{}{reset} to view recently used applications. Press {blue}tab{reset} and {blue}grave{reset} / {blue}shift{reset} + {blue}tab{reset} to select a different window, release {bold}{blue}{}{reset} to close the window.\n",
98                switch.modifier,
99                switch.modifier,
100                switch.modifier,
101            ));
102        } else {
103            let _ = builder.write_str(&format!("{italic}<Switch mode disabled>{reset}\n"));
104        }
105    } else {
106        let _ = builder.write_str(&format!("{italic}<Windows disabled>{reset}\n"));
107    }
108
109    builder
110}
111
112#[cfg(test)]
113mod tests {
114    use super::*;
115    use crate::structs::*;
116    use std::path::PathBuf;
117
118    fn create_test_config() -> Config {
119        Config {
120            windows: Some(Windows {
121                overview: Some(Overview::default()),
122                switch: Some(Switch::default()),
123                ..Default::default()
124            }),
125            ..Default::default()
126        }
127    }
128
129    #[test_log::test]
130    #[test_log(default_log_filter = "trace")]
131    fn test_explain_with_overview() {
132        const CONFIG: &str = r"Config is valid (/test/config.ron)
133Explanation (blue are keys, bold blue keys can be configured in config):
134Use Super + super_l to open the Overview. Use tab and grave / shift + tab to select a different window, press return to switch
135You can also use the arrow keys or Ctrl + vim keys to navigate the workspaces. Use Esc to close the overview.
136After opening the Overview the Launcher is available:
137	- Start typing to search through applications (sorted by how often they were opened). Press return to launch the first app, use Ctrl + 1/2/3/... to open the second, third, etc.
138	- Press Ctrl + t to run the typed command in a terminal.
139	- Press Ctrl + <key> to search the typed text in any of the configured SearchEngines: Google, Wikipedia.
140	- Typing a mathematical expression will calculate it and display the result in the launcher.
141	- Paths (starting with ~ or /) can be open in default file-manager.
142	- Type Reboot/Shutdown/etc. to run corresponding commands. Type `actions` to see all available ones.
143
144Press Alt + tab and hold Alt to view recently used applications. Press tab and grave / shift + tab to select a different window, release Alt to close the window.
145";
146        let config = create_test_config();
147        let path = PathBuf::from("/test/config.ron");
148        let result = explain(&config, &path, false, true);
149        assert_eq!(result, CONFIG);
150    }
151
152    #[test_log::test]
153    #[test_log(default_log_filter = "trace")]
154    fn test_explain_without_overview() {
155        const CONFIG: &str = r"Config is valid (/test/config.ron)
156Explanation (blue are keys, bold blue keys can be configured in config):
157<Overview disabled>
158
159Press Alt + tab and hold Alt to view recently used applications. Press tab and grave / shift + tab to select a different window, release Alt to close the window.
160";
161        let mut config = create_test_config();
162        config
163            .windows
164            .as_mut()
165            .expect("config option missing")
166            .overview = None;
167        let path = PathBuf::from("/test/config.ron");
168        let result = explain(&config, &path, false, true);
169        assert_eq!(result, CONFIG);
170    }
171
172    #[test_log::test]
173    #[test_log(default_log_filter = "trace")]
174    fn test_explain_without_switch() {
175        const CONFIG: &str = r"Config is valid (/test/config.ron)
176Explanation (blue are keys, bold blue keys can be configured in config):
177Use Super + super_l to open the Overview. Use tab and grave / shift + tab to select a different window, press return to switch
178You can also use the arrow keys or Ctrl + vim keys to navigate the workspaces. Use Esc to close the overview.
179After opening the Overview the Launcher is available:
180	- Start typing to search through applications (sorted by how often they were opened). Press return to launch the first app, use Ctrl + 1/2/3/... to open the second, third, etc.
181	- Press Ctrl + t to run the typed command in a terminal.
182	- Press Ctrl + <key> to search the typed text in any of the configured SearchEngines: Google, Wikipedia.
183	- Typing a mathematical expression will calculate it and display the result in the launcher.
184	- Paths (starting with ~ or /) can be open in default file-manager.
185	- Type Reboot/Shutdown/etc. to run corresponding commands. Type `actions` to see all available ones.
186
187<Switch mode disabled>
188";
189        let mut config = create_test_config();
190        config
191            .windows
192            .as_mut()
193            .expect("config option missing")
194            .switch = None;
195        let path = PathBuf::from("/test/config.ron");
196        let result = explain(&config, &path, false, true);
197        assert_eq!(result, CONFIG);
198    }
199
200    #[test_log::test]
201    #[test_log(default_log_filter = "trace")]
202    fn test_explain_without_plugins() {
203        const CONFIG: &str = r"Use Super + super_l to open the Overview. Use tab and grave / shift + tab to select a different window, press return to switch
204You can also use the arrow keys or Ctrl + vim keys to navigate the workspaces. Use Esc to close the overview.
205After opening the Overview the Launcher is available:
206	<No plugins enabled in launcher>
207
208Press Alt + tab and hold Alt to view recently used applications. Press tab and grave / shift + tab to select a different window, release Alt to close the window.
209";
210        let mut config = create_test_config();
211        config
212            .windows
213            .as_mut()
214            .expect("config option missing")
215            .overview
216            .as_mut()
217            .expect("config option missing")
218            .launcher
219            .plugins = Plugins {
220            applications: None,
221            terminal: None,
222            shell: None,
223            websearch: None,
224            calc: None,
225            path: None,
226            actions: None,
227        };
228        let path = PathBuf::from("/test/config.ron");
229        let result = explain(&config, &path, false, false);
230        assert_eq!(result, CONFIG);
231    }
232}