zellij_utils/input/
cli_assets.rs1use crate::data::LayoutInfo;
2use crate::input::options::Options;
3use crate::pane_size::Size;
4use crate::{
5 home::{find_default_config_dir, get_theme_dir},
6 input::{config::Config, layout::Layout, theme::Themes},
7 setup::get_default_themes,
8};
9use serde::{Deserialize, Serialize};
10use std::collections::BTreeMap;
11use std::path::PathBuf;
12
13pub const HOST_TERMINAL_ENV_VARS: [&str; 7] = [
14 "TERM",
15 "TERM_PROGRAM",
16 "TERM_PROGRAM_VERSION",
17 "KITTY_WINDOW_ID",
18 "WEZTERM_PANE",
19 "ITERM_SESSION_ID",
20 "GHOSTTY_RESOURCES_DIR",
21];
22
23pub fn host_terminal_env() -> BTreeMap<String, String> {
24 host_terminal_env_from(|name| std::env::var(name).ok())
25}
26
27pub fn host_terminal_env_from<F: Fn(&str) -> Option<String>>(
28 lookup: F,
29) -> BTreeMap<String, String> {
30 HOST_TERMINAL_ENV_VARS
31 .iter()
32 .filter_map(|name| lookup(name).map(|value| (name.to_string(), value)))
33 .collect()
34}
35
36#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
37pub struct CliAssets {
38 pub config_file_path: Option<PathBuf>,
39 pub config_dir: Option<PathBuf>,
40 pub should_ignore_config: bool,
41 pub configuration_options: Option<Options>, pub layout: Option<LayoutInfo>,
43 pub terminal_window_size: Size,
44 pub data_dir: Option<PathBuf>,
45 pub is_debug: bool,
46 pub max_panes: Option<usize>,
47 pub force_run_layout_commands: bool,
48 pub cwd: Option<PathBuf>,
49 pub host_terminal_env: BTreeMap<String, String>,
50}
51
52impl CliAssets {
53 pub fn load_config_and_layout(&self) -> (Config, Layout) {
54 let config = {
55 if self.should_ignore_config {
56 Config::from_default_assets().unwrap_or_else(|_| Default::default())
57 } else if let Some(ref path) = self.config_file_path {
58 let default_config =
59 Config::from_default_assets().unwrap_or_else(|_| Default::default());
60 Config::from_path(path, Some(default_config.clone()))
61 .unwrap_or_else(|_| default_config)
62 } else {
63 Config::from_default_assets().unwrap_or_else(|_| Default::default())
64 }
65 };
66
67 let (mut layout, mut config_with_merged_layout_opts) = {
68 let layout_dir = self
69 .configuration_options
70 .as_ref()
71 .and_then(|e| e.layout_dir.clone())
72 .or_else(|| config.options.layout_dir.clone())
73 .or_else(|| {
74 self.config_dir
75 .clone()
76 .or_else(find_default_config_dir)
77 .map(|dir| dir.join("layouts"))
78 });
79 self.layout.as_ref().and_then(|layout_info| {
80 Layout::from_layout_info_with_config(&layout_dir, layout_info, Some(config.clone()))
81 .ok()
82 })
83 }
84 .map(|(layout, config)| (layout, config))
85 .unwrap_or_else(|| (Layout::default_layout_asset(), config));
86
87 if self.force_run_layout_commands {
88 layout.recursively_add_start_suspended(Some(false));
89 }
90
91 config_with_merged_layout_opts.themes = config_with_merged_layout_opts
92 .themes
93 .merge(get_default_themes());
94
95 let user_theme_dir = self
96 .configuration_options
97 .as_ref()
98 .and_then(|o| o.theme_dir.clone())
99 .or_else(|| {
100 config_with_merged_layout_opts
101 .options
102 .theme_dir
103 .clone()
104 .or_else(|| {
105 get_theme_dir(self.config_dir.clone().or_else(find_default_config_dir))
106 })
107 .filter(|dir| dir.exists())
108 });
109 if let Some(themes) = user_theme_dir.and_then(|u| Themes::from_dir(u).ok()) {
110 config_with_merged_layout_opts.themes =
111 config_with_merged_layout_opts.themes.merge(themes);
112 }
113
114 (config_with_merged_layout_opts, layout)
115 }
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121
122 #[test]
123 fn only_whitelisted_host_terminal_variables_are_captured() {
124 let env = host_terminal_env_from(|name| match name {
125 "TERM" => Some("xterm-kitty".to_owned()),
126 "KITTY_WINDOW_ID" => Some("3".to_owned()),
127 "SECRET_TOKEN" => Some("hunter2".to_owned()),
128 _ => None,
129 });
130 assert_eq!(
131 env,
132 [
133 ("TERM".to_owned(), "xterm-kitty".to_owned()),
134 ("KITTY_WINDOW_ID".to_owned(), "3".to_owned())
135 ]
136 .into_iter()
137 .collect::<BTreeMap<String, String>>(),
138 "unset and non-whitelisted variables are left out"
139 );
140 }
141
142 #[test]
143 fn a_host_without_any_of_the_known_variables_yields_an_empty_env() {
144 assert!(host_terminal_env_from(|_| None).is_empty());
145 }
146}