Skip to main content

Mountain/IPC/WindServiceHandlers/Terminal/
LocalPTYGetProfiles.rs

1#![allow(non_snake_case)]
2
3//! Discover available terminal profiles. Probes every well-
4//! known shell location plus `/etc/shells` (Unix) or known
5//! Windows install paths. The first existing match flags
6//! `isDefault=true`; on Unix the user's `$SHELL` wins.
7//!
8//! The wire shape matches VS Code's
9//! `ITerminalProfileProvider.profileName / path / Arguments /
10//! env / icon / isDefault` so Wind's terminal picker renders
11//! without reshaping.
12
13use serde_json::{Value, json};
14
15pub async fn LocalPTYGetProfiles() -> Result<Value, String> {
16	let mut Profiles = Vec::new();
17
18	#[cfg(unix)]
19	{
20		let DefaultShell = std::env::var("SHELL").unwrap_or_else(|_| "/bin/sh".to_string());
21
22		let UnixShells = [
23			"/bin/zsh",
24			"/bin/bash",
25			"/bin/sh",
26			"/usr/bin/zsh",
27			"/usr/bin/bash",
28			"/usr/bin/fish",
29			"/usr/local/bin/fish",
30			"/usr/local/bin/zsh",
31			"/usr/local/bin/bash",
32			"/bin/dash",
33			"/usr/bin/ksh",
34			"/usr/bin/tcsh",
35			"/bin/csh",
36			"/usr/bin/pwsh",
37			"/usr/local/bin/pwsh",
38		];
39
40		for Shell in &UnixShells {
41			if std::path::Path::new(Shell).exists() {
42				let Name = std::path::Path::new(Shell)
43					.file_name()
44					.and_then(|N| N.to_str())
45					.unwrap_or("shell");
46
47				Profiles.push(json!({
48					"profileName": Name,
49					"path": Shell,
50					"isDefault": *Shell == DefaultShell.as_str(),
51					"Arguments": [],
52					"env": {},
53					"icon": "terminal"
54				}));
55			}
56		}
57
58		if let Ok(ShellsFile) = std::fs::read_to_string("/etc/shells") {
59			for Line in ShellsFile.lines() {
60				let Trimmed = Line.trim();
61
62				if Trimmed.starts_with('/') && !Trimmed.starts_with('#') {
63					let AlreadyAdded = Profiles.iter().any(|P| P.get("path").and_then(|V| V.as_str()) == Some(Trimmed));
64
65					if !AlreadyAdded && std::path::Path::new(Trimmed).exists() {
66						let Name = std::path::Path::new(Trimmed)
67							.file_name()
68							.and_then(|N| N.to_str())
69							.unwrap_or("shell");
70
71						Profiles.push(json!({
72							"profileName": Name,
73							"path": Trimmed,
74							"isDefault": Trimmed == DefaultShell.as_str(),
75							"Arguments": [],
76							"env": {},
77							"icon": "terminal"
78						}));
79					}
80				}
81			}
82		}
83	}
84
85	#[cfg(target_os = "windows")]
86	{
87		let SystemRoot = std::env::var("SystemRoot").unwrap_or_else(|_| "C:\\Windows".to_string());
88
89		let ProgramFiles = std::env::var("ProgramFiles").unwrap_or_else(|_| "C:\\Program Files".to_string());
90
91		let LocalAppData =
92			std::env::var("LOCALAPPDATA").unwrap_or_else(|_| "C:\\Users\\User\\AppData\\Local".to_string());
93
94		let WindowsShells:Vec<(&str, String, Vec<&str>)> = vec![
95			(
96				"PowerShell",
97				format!("{}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", SystemRoot),
98				vec!["-NoLogo"],
99			),
100			(
101				"PowerShell 7",
102				format!("{}\\PowerShell\\7\\pwsh.exe", ProgramFiles),
103				vec!["-NoLogo"],
104			),
105			("Command Prompt", format!("{}\\System32\\cmd.exe", SystemRoot), vec![]),
106			(
107				"Git Bash",
108				format!("{}\\Git\\bin\\bash.exe", ProgramFiles),
109				vec!["--login", "-i"],
110			),
111			(
112				"Git Bash (User)",
113				format!("{}\\Programs\\Git\\bin\\bash.exe", LocalAppData),
114				vec!["--login", "-i"],
115			),
116			("WSL", format!("{}\\System32\\wsl.exe", SystemRoot), vec![]),
117			("MSYS2", "C:\\msys64\\usr\\bin\\bash.exe".to_string(), vec!["--login", "-i"]),
118			("Cygwin", "C:\\cygwin64\\bin\\bash.exe".to_string(), vec!["--login", "-i"]),
119		];
120
121		let mut IsFirstFound = true;
122
123		for (Name, Path, Args) in &WindowsShells {
124			if std::path::Path::new(Path).exists() {
125				Profiles.push(json!({
126					"profileName": Name,
127					"path": Path,
128					"isDefault": IsFirstFound,
129					"Arguments": Args,
130					"env": {},
131					"icon": "terminal"
132				}));
133
134				IsFirstFound = false;
135			}
136		}
137	}
138
139	Ok(json!(Profiles))
140}