synaps_cli/tools/shell/
config.rs1use std::time::Duration;
4
5#[derive(Debug, Clone)]
7pub struct ShellConfig {
8 pub max_sessions: usize,
9 pub idle_timeout: Duration,
10 pub readiness_timeout_ms: u64,
11 pub max_readiness_timeout_ms: u64,
12 pub default_rows: u16,
13 pub default_cols: u16,
14 pub prompt_patterns: Vec<String>,
15 pub readiness_strategy: String,
16 pub max_output: usize,
17}
18
19impl Default for ShellConfig {
20 fn default() -> Self {
21 Self {
22 max_sessions: 5,
23 idle_timeout: Duration::from_secs(600),
24 readiness_timeout_ms: 300,
25 max_readiness_timeout_ms: 10_000,
26 default_rows: 24,
27 default_cols: 80,
28 prompt_patterns: default_prompt_patterns(),
29 readiness_strategy: "hybrid".to_string(),
30 max_output: 30000,
31 }
32 }
33}
34
35fn default_prompt_patterns() -> Vec<String> {
36 vec![
37 r"[$#%»] $".into(),
38 r"[$#%»]\s*$".into(),
39 r"\(gdb\)\s*$".into(),
40 r">>>\s*$".into(),
41 r"\.\.\.\:\s*$".into(),
42 r"In \[\d+\]:\s*$".into(),
43 r"irb.*>\s*$".into(),
44 r"mysql>\s*$".into(),
45 r"postgres[=#]>\s*$".into(),
46 r"Password:\s*$".into(),
47 r"\[Y/n\]\s*$".into(),
48 r"\(yes/no.*\)\?\s*$".into(),
49 r"% $".into(),
50 r"% \s*$".into(),
51 r"root@[\w.-]+:[/~].*# $".into(),
52 r"[\w.-]+@[\w.-]+:[/~].*\$ $".into(),
53 r"Enter passphrase.*:\s*$".into(),
54 r"Token:\s*$".into(),
55 r"Verification code:\s*$".into(),
56 ]
57}
58
59#[cfg(test)]
60mod tests {
61 use super::*;
62 use serial_test::serial;
63 use crate::core::config::load_config;
64
65 #[test]
66 fn test_shell_config_default() {
67 let config = ShellConfig::default();
68 assert_eq!(config.max_sessions, 5);
69 assert_eq!(config.idle_timeout, Duration::from_secs(600));
70 assert_eq!(config.readiness_timeout_ms, 300);
71 assert_eq!(config.max_readiness_timeout_ms, 10_000);
72 assert_eq!(config.default_rows, 24);
73 assert_eq!(config.default_cols, 80);
74 assert_eq!(config.prompt_patterns.len(), 19);
75 }
76
77 #[test]
78 #[serial]
79 fn test_shell_config_from_file() {
80 {
85 let test_dir = std::path::PathBuf::from("/tmp/synaps-shell-test-1/.synaps-cli");
86 let _ = std::fs::create_dir_all(&test_dir);
87 let config_path = test_dir.join("config");
88
89 let config_content = "shell.max_sessions = 10\nshell.idle_timeout = 1200\nshell.readiness_timeout_ms = 500\nshell.max_readiness_timeout_ms = 20000\nshell.default_rows = 30\nshell.default_cols = 120\n";
90 std::fs::write(&config_path, config_content).unwrap();
91
92 let original_home = std::env::var("HOME").ok();
93 let original_base_dir = std::env::var("SYNAPS_BASE_DIR").ok();
94 std::env::remove_var("SYNAPS_BASE_DIR");
95 std::env::set_var("HOME", "/tmp/synaps-shell-test-1");
96 let config = load_config();
97 if let Some(home) = original_home {
98 std::env::set_var("HOME", home);
99 }
100 if let Some(base_dir) = original_base_dir {
101 std::env::set_var("SYNAPS_BASE_DIR", base_dir);
102 }
103 let _ = std::fs::remove_dir_all("/tmp/synaps-shell-test-1");
104
105 assert_eq!(config.shell.max_sessions, 10);
106 assert_eq!(config.shell.idle_timeout, Duration::from_secs(1200));
107 assert_eq!(config.shell.readiness_timeout_ms, 500);
108 assert_eq!(config.shell.max_readiness_timeout_ms, 20000);
109 assert_eq!(config.shell.default_rows, 30);
110 assert_eq!(config.shell.default_cols, 120);
111 }
112
113 {
115 let test_dir = std::path::PathBuf::from("/tmp/synaps-shell-test-2/.synaps-cli");
116 let _ = std::fs::create_dir_all(&test_dir);
117 let config_path = test_dir.join("config");
118
119 let config_content = "shell.max_sessions = 3\nshell.default_rows = 40\n";
120 std::fs::write(&config_path, config_content).unwrap();
121
122 let original_home = std::env::var("HOME").ok();
123 let original_base_dir = std::env::var("SYNAPS_BASE_DIR").ok();
124 std::env::remove_var("SYNAPS_BASE_DIR");
125 std::env::set_var("HOME", "/tmp/synaps-shell-test-2");
126 let config = load_config();
127 if let Some(home) = original_home {
128 std::env::set_var("HOME", home);
129 }
130 if let Some(base_dir) = original_base_dir {
131 std::env::set_var("SYNAPS_BASE_DIR", base_dir);
132 }
133 let _ = std::fs::remove_dir_all("/tmp/synaps-shell-test-2");
134
135 assert_eq!(config.shell.max_sessions, 3);
136 assert_eq!(config.shell.default_rows, 40);
137 assert_eq!(config.shell.idle_timeout, Duration::from_secs(600));
138 assert_eq!(config.shell.readiness_timeout_ms, 300);
139 }
140
141 {
143 let test_dir = std::path::PathBuf::from("/tmp/synaps-shell-test-3/.synaps-cli");
144 let _ = std::fs::create_dir_all(&test_dir);
145 let config_path = test_dir.join("config");
146
147 let config_content = "shell.max_sessions = not_a_number\nshell.idle_timeout = invalid\n";
148 std::fs::write(&config_path, config_content).unwrap();
149
150 let original_home = std::env::var("HOME").ok();
151 let original_base_dir = std::env::var("SYNAPS_BASE_DIR").ok();
152 std::env::remove_var("SYNAPS_BASE_DIR");
153 std::env::set_var("HOME", "/tmp/synaps-shell-test-3");
154 let config = load_config();
155 if let Some(home) = original_home {
156 std::env::set_var("HOME", home);
157 }
158 if let Some(base_dir) = original_base_dir {
159 std::env::set_var("SYNAPS_BASE_DIR", base_dir);
160 }
161 let _ = std::fs::remove_dir_all("/tmp/synaps-shell-test-3");
162
163 assert_eq!(config.shell.max_sessions, 5);
164 assert_eq!(config.shell.idle_timeout, Duration::from_secs(600));
165 }
166 }
167}