Skip to main content

Mountain/Binary/IPC/
ProcessCommand.rs

1//! # ProcessCommand
2//!
3//! Tauri commands for Wind's ProcessPolyfill.
4//! These are invoked directly (not through MountainIPCInvoke) as separate
5//! Tauri commands: process:get_exec_path, process:get_platform, etc.
6
7use std::collections::HashMap;
8
9use serde_json::{Value, json};
10
11/// Get the executable path of the current process.
12#[tauri::command]
13pub async fn process_get_exec_path() -> Result<String, String> {
14	std::env::current_exe()
15		.map(|P| P.to_string_lossy().to_string())
16		.map_err(|E| format!("Failed to get exec path: {}", E))
17}
18
19/// Get the current platform identifier.
20#[tauri::command]
21pub async fn process_get_platform() -> Result<String, String> {
22	Ok(match std::env::consts::OS {
23		"macos" => "darwin",
24		"windows" => "win32",
25		"linux" => "linux",
26		Other => Other,
27	}
28	.to_string())
29}
30
31/// Get the CPU architecture.
32#[tauri::command]
33pub async fn process_get_arch() -> Result<String, String> { Ok(std::env::consts::ARCH.to_string()) }
34
35/// Get the process ID.
36#[tauri::command]
37pub async fn process_get_pid() -> Result<u32, String> { Ok(std::process::id()) }
38
39/// Get the shell environment variables.
40/// Returns the full environment of the Mountain process, which inherits
41/// the user's shell environment on all platforms.
42#[tauri::command]
43pub async fn process_get_shell_env() -> Result<HashMap<String, String>, String> { Ok(std::env::vars().collect()) }
44
45/// Get process memory information.
46/// Returns private, shared, and residentSet memory in bytes.
47#[tauri::command]
48pub async fn process_get_memory_info() -> Result<Value, String> {
49	#[cfg(target_os = "macos")]
50	{
51		// macOS: use mach_task_basic_info or fallback to ps
52		let Output = std::process::Command::new("ps")
53			.args(["-o", "rss=,vsz=", "-p", &std::process::id().to_string()])
54			.output();
55
56		match Output {
57			Ok(Out) => {
58				let Text = String::from_utf8_lossy(&Out.stdout);
59				let Parts:Vec<&str> = Text.split_whitespace().collect();
60				let Rss = Parts.first().and_then(|V| V.parse::<u64>().ok()).unwrap_or(0) * 1024;
61				let Vsz = Parts.get(1).and_then(|V| V.parse::<u64>().ok()).unwrap_or(0) * 1024;
62				Ok(json!({
63					"private": Rss,
64					"shared": 0,
65					"residentSet": Rss
66				}))
67			},
68			Err(_) => Ok(json!({ "private": 0, "shared": 0, "residentSet": 0 })),
69		}
70	}
71
72	#[cfg(target_os = "windows")]
73	{
74		// Windows: read from /proc/self/status equivalent or tasklist
75		let Output = std::process::Command::new("tasklist")
76			.args(["/FI", &format!("PID eq {}", std::process::id()), "/FO", "CSV", "/NH"])
77			.output();
78
79		match Output {
80			Ok(Out) => {
81				let Text = String::from_utf8_lossy(&Out.stdout);
82				// Parse "Image Name","PID","Session Name","Session#","Mem Usage"
83				let MemStr = Text.split(',').nth(4).unwrap_or("\"0 K\"");
84				let MemKb:u64 = MemStr
85					.chars()
86					.filter(|C| C.is_ascii_digit())
87					.collect::<String>()
88					.parse()
89					.unwrap_or(0);
90				let MemBytes = MemKb * 1024;
91				Ok(json!({
92					"private": MemBytes,
93					"shared": 0,
94					"residentSet": MemBytes
95				}))
96			},
97			Err(_) => Ok(json!({ "private": 0, "shared": 0, "residentSet": 0 })),
98		}
99	}
100
101	#[cfg(target_os = "linux")]
102	{
103		// Linux: read /proc/self/statm
104		match tokio::fs::read_to_string("/proc/self/statm").await {
105			Ok(Content) => {
106				let Parts:Vec<&str> = Content.split_whitespace().collect();
107				let PageSize:u64 = 4096; // typical page size
108				let Vsz = Parts.first().and_then(|V| V.parse::<u64>().ok()).unwrap_or(0) * PageSize;
109				let Rss = Parts.get(1).and_then(|V| V.parse::<u64>().ok()).unwrap_or(0) * PageSize;
110				let Shared = Parts.get(2).and_then(|V| V.parse::<u64>().ok()).unwrap_or(0) * PageSize;
111				Ok(json!({
112					"private": Rss.saturating_sub(Shared),
113					"shared": Shared,
114					"residentSet": Rss
115				}))
116			},
117			Err(_) => Ok(json!({ "private": 0, "shared": 0, "residentSet": 0 })),
118		}
119	}
120
121	#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "linux")))]
122	{
123		Ok(json!({ "private": 0, "shared": 0, "residentSet": 0 }))
124	}
125}