Mountain/ProcessManagement/
InitializationData.rs

1// File: Mountain/Source/ProcessManagement/InitializationData.rs
2// Role: Constructs initial data payloads for the `Sky` frontend and `Cocoon`
3// sidecar. Responsibilities:
4//   - `ConstructSandboxConfiguration`: Gathers host environment data for the
5//     frontend.
6//   - `ConstructExtensionHostInitializationData`: Assembles all necessary data
7//     for the extension host to initialize, including extensions, workspace
8//     info, and paths.
9
10//! # InitializationData
11//!
12//! Contains the logic for constructing the initial data payloads that are sent
13//! to the `Sky` frontend and the `Cocoon` sidecar to bootstrap their states.
14
15#![allow(non_snake_case, non_camel_case_types)]
16
17use std::{collections::HashMap, env, sync::Arc};
18
19use Common::{
20	Environment::Requires::Requires,
21	Error::CommonError::CommonError,
22	ExtensionManagement::ExtensionManagementService::ExtensionManagementService,
23	WorkSpace::WorkSpaceProvider::WorkSpaceProvider,
24};
25use log::info;
26use serde_json::{Value, json};
27use tauri::{AppHandle, Manager, Wry};
28use uuid::Uuid;
29
30use crate::{
31	ApplicationState::ApplicationState::ApplicationState,
32	Environment::MountainEnvironment::MountainEnvironment,
33};
34
35/// Constructs the `ISandboxConfiguration` payload needed by the `Sky` frontend.
36pub async fn ConstructSandboxConfiguration(
37	ApplicationHandle:&AppHandle<Wry>,
38
39	ApplicationState:&Arc<ApplicationState>,
40) -> Result<Value, CommonError> {
41	info!("[InitializationData] Constructing ISandboxConfiguration for Sky.");
42
43	let PathResolver = ApplicationHandle.path();
44
45	let AppRootUri = PathResolver.resource_dir().map_err(|Error| {
46		CommonError::ConfigurationLoad {
47			Description:format!("Failed to resolve resource directory (app root): {}", Error),
48		}
49	})?;
50
51	let AppDataDir = PathResolver.app_data_dir().map_err(|Error| {
52		CommonError::ConfigurationLoad { Description:format!("Failed to resolve app data directory: {}", Error) }
53	})?;
54
55	let HomeDir = PathResolver.home_dir().map_err(|Error| {
56		CommonError::ConfigurationLoad { Description:format!("Failed to resolve home directory: {}", Error) }
57	})?;
58
59	let TmpDir = env::temp_dir();
60
61	let BackupPath = AppDataDir.join("Backups").join(ApplicationState.GetWorkSpaceIdentifier()?);
62
63	let Platform = match env::consts::OS {
64		"windows" => "win32",
65
66		"macos" => "darwin",
67
68		"linux" => "linux",
69
70		_ => "unknown",
71	};
72
73	let Arch = match env::consts::ARCH {
74		"x86_64" => "x64",
75
76		"aarch64" => "arm64",
77
78		"x86" => "ia32",
79
80		_ => "unknown",
81	};
82
83	let Versions = json!({
84		"mountain": ApplicationHandle.package_info().version.to_string(),
85
86		// Explicitly signal we are not in Electron
87		"electron": "0.0.0-tauri",
88
89		// Representative version
90		"chrome": "120.0.0.0",
91
92		// Representative version
93		"node": "18.18.2"
94	});
95
96	Ok(json!({
97		"windowId": ApplicationHandle.get_webview_window("main").unwrap().label(),
98
99		// TODO: Persist and read from storage
100		"machineId": Uuid::new_v4().to_string(),
101
102		"sessionId": Uuid::new_v4().to_string(),
103
104		"logLevel": log::max_level() as i32,
105
106		"userEnv": env::vars().collect::<HashMap<_,_>>(),
107
108		"appRoot": url::Url::from_directory_path(AppRootUri).unwrap().to_string(),
109
110		"appName": ApplicationHandle.package_info().name.clone(),
111
112		"appUriScheme": "mountain",
113
114		"appLanguage": "en",
115
116		"appHost": "desktop",
117
118		"platform": Platform,
119
120		"arch": Arch,
121
122		"versions": Versions,
123
124		"execPath": env::current_exe().unwrap_or_default().to_string_lossy(),
125
126		"homeDir": url::Url::from_directory_path(HomeDir).unwrap().to_string(),
127
128		"tmpDir": url::Url::from_directory_path(TmpDir).unwrap().to_string(),
129
130		"userDataDir": url::Url::from_directory_path(AppDataDir).unwrap().to_string(),
131
132		"backupPath": url::Url::from_directory_path(BackupPath).unwrap().to_string(),
133
134		"nls": { "messages": {}, "language": "en", "availableLanguages": { "en": "English" } },
135
136		"productConfiguration": {
137
138			"nameShort": "Mountain",
139
140			"nameLong": "Mountain Editor",
141
142			"applicationName": "mountain",
143
144			"embedderIdentifier": "mountain-desktop"
145		},
146
147		"resourcesPath": PathResolver.resource_dir().unwrap_or_default().to_string_lossy(),
148
149		"VSCODE_CWD": env::current_dir().unwrap_or_default().to_string_lossy(),
150	}))
151}
152
153/// Constructs the `IExtensionHostInitData` payload sent to `Cocoon`.
154pub async fn ConstructExtensionHostInitializationData(Environment:&MountainEnvironment) -> Result<Value, CommonError> {
155	info!("[InitializationData] Constructing IExtensionHostInitData for Cocoon.");
156
157	let ApplicationState = &Environment.ApplicationState;
158
159	let ApplicationHandle = &Environment.ApplicationHandle;
160
161	let ExtensionManagementProvider:Arc<dyn ExtensionManagementService> = Environment.Require();
162
163	let ExtensionsDTO = ExtensionManagementProvider.GetExtensions().await?;
164
165	let WorkspaceProvider:Arc<dyn WorkSpaceProvider> = Environment.Require();
166
167	let WorkspaceName = WorkspaceProvider
168		.GetWorkSpaceName()
169		.await?
170		.unwrap_or_else(|| "Mountain WorkSpace".to_string());
171
172	let WorkSpaceFoldersGuard = ApplicationState.WorkSpaceFolders.lock().unwrap();
173
174	let WorkSpaceDTO = if WorkSpaceFoldersGuard.is_empty() {
175		Value::Null
176	} else {
177		json!({
178
179			"id": ApplicationState.GetWorkSpaceIdentifier()?,
180
181			"name": WorkspaceName,
182
183			"configuration": ApplicationState.WorkSpaceConfigurationPath.lock().unwrap().as_ref().map(|p| p.to_string_lossy()),
184
185			"isUntitled": ApplicationState.WorkSpaceConfigurationPath.lock().unwrap().is_none(),
186
187			"transient": false
188		})
189	};
190
191	let PathResolver = ApplicationHandle.path();
192
193	let AppRoot = PathResolver
194		.resource_dir()
195		.map_err(|Error| CommonError::ConfigurationLoad { Description:Error.to_string() })?;
196
197	let AppData = PathResolver
198		.app_data_dir()
199		.map_err(|Error| CommonError::ConfigurationLoad { Description:Error.to_string() })?;
200
201	let LogsLocation = PathResolver
202		.app_log_dir()
203		.map_err(|Error| CommonError::ConfigurationLoad { Description:Error.to_string() })?;
204
205	let GlobalStorage = AppData.join("User/globalStorage");
206
207	let WorkSpaceStorage = AppData.join("User/workspaceStorage");
208
209	Ok(json!({
210
211		"commit": "dev-commit-hash",
212
213		"version": ApplicationHandle.package_info().version.to_string(),
214
215		"quality": "development",
216
217		"parentPid": std::process::id(),
218
219		"environment": {
220
221			"isExtensionDevelopmentDebug": false,
222
223			"appName": "Mountain",
224
225			"appHost": "desktop",
226
227			"appUriScheme": "mountain",
228
229			"appLanguage": "en",
230
231			"isExtensionTelemetryLoggingOnly": true,
232
233			"appRoot": url::Url::from_directory_path(AppRoot.clone()).unwrap(),
234
235			"globalStorageHome": url::Url::from_directory_path(GlobalStorage).unwrap(),
236
237			"workspaceStorageHome": url::Url::from_directory_path(WorkSpaceStorage).unwrap(),
238
239			"extensionDevelopmentLocationURI": [],
240
241			"extensionTestsLocationURI": Value::Null,
242
243			"extensionLogLevel": [["info", "Default"]],
244
245		},
246
247		"workspace": WorkSpaceDTO,
248
249		"remote": {
250
251			"isRemote": false,
252
253			"authority": Value::Null,
254
255			"connectionData": Value::Null,
256
257		},
258
259		"consoleForward": { "includeStack": true, "logNative": true },
260
261		"logLevel": log::max_level() as i32,
262
263		"logsLocation": url::Url::from_directory_path(LogsLocation).unwrap(),
264
265		"telemetryInfo": {
266
267			"sessionId": Uuid::new_v4().to_string(),
268
269			"machineId": Uuid::new_v4().to_string(),
270
271			"firstSessionDate": "2024-01-01T00:00:00.000Z",
272
273			"msftInternal": false
274		},
275
276		"extensions": ExtensionsDTO,
277
278		"autoStart": true,
279
280		// UIKind.Desktop
281		"uiKind": 1,
282	}))
283}