1#![allow(non_snake_case, non_camel_case_types)]
33
34use std::{
35 path::PathBuf,
36 sync::{Arc, Mutex},
37};
38
39use Echo::Scheduler::SchedulerBuilder::SchedulerBuilder;
40use log::{LevelFilter, debug, error, info, trace, warn};
41use tauri::{AppHandle, Manager, RunEvent, Wry};
42use tauri_plugin_log::{RotationStrategy, Target, TargetKind, TimezoneStrategy};
43
44use crate::{
45 ApplicationState::{
46 ApplicationState::{ApplicationState, MapLockError},
47 Internal::ScanAndPopulateExtensions,
48 },
49 Command,
50 Environment::{ConfigurationProvider::InitializeAndMergeConfigurations, MountainEnvironment::MountainEnvironment},
51 ProcessManagement::{CocoonManagement::InitializeCocoon, InitializationData},
52 RunTime::ApplicationRunTime::ApplicationRunTime,
53 Vine,
54};
55
56macro_rules! TraceStep {
62 ($($arg:tt)*) => {{
63 trace!($($arg)*);
64 }};
65}
66
67#[tauri::command]
74async fn MountainGetWorkbenchConfiguration(
75 ApplicationHandle:AppHandle<Wry>,
76 State:tauri::State<'_, Arc<ApplicationState>>,
77) -> Result<serde_json::Value, String> {
78 info!("[IPC] [WorkbenchConfig] Request received.");
79
80 debug!("[IPC] [WorkbenchConfig] Constructing sandbox configuration...");
81
82 let Config = InitializationData::ConstructSandboxConfiguration(&ApplicationHandle, &State)
83 .await
84 .map_err(|Error| {
85 error!("[IPC] [WorkbenchConfig] Failed: {}", Error);
86
87 Error.to_string()
88 })?;
89
90 debug!("[IPC] [WorkbenchConfig] Success. Returning payload.");
91
92 Ok(Config)
93}
94
95pub fn Fn() {
101 TraceStep!("[Boot] [Runtime] Building Tokio runtime...");
105
106 let Runtime = tokio::runtime::Builder::new_multi_thread()
107 .enable_all()
108 .build()
109 .expect("FATAL: Cannot build Tokio runtime.");
110
111 TraceStep!("[Boot] [Runtime] Tokio runtime built.");
112
113 Runtime.block_on(async {
114 debug!("[Boot] [Args] Collecting CLI args...");
118
119 let CliArgs:Vec<String> = std::env::args().collect();
120
121 debug!("[Boot] [Args] CLI Args: {:?}", CliArgs);
122
123 let WorkSpacePathArgument = CliArgs.iter().find(|Arg| Arg.ends_with(".code-workspace"));
124
125 TraceStep!("[Boot] [Args] Workspace arg present: {}", WorkSpacePathArgument.is_some());
126
127 let (InitialFolders, WorkSpaceConfigurationPath) = if let Some(PathString) = WorkSpacePathArgument {
128 let Path = PathBuf::from(PathString);
129
130 println!("[Boot] [Args] Found workspace argument: {}", Path.display());
131
132 debug!("[Boot] [Workspace] Reading workspace file: {}", Path.display());
133
134 match std::fs::read_to_string(&Path) {
135 Ok(Content) => {
136 debug!("[Boot] [Workspace] Workspace file read ok ({} bytes).", Content.len());
137
138 crate::WorkSpace::WorkSpaceFileService::ParseWorkSpaceFile(&Path, &Content)
139 .map(|Folders| {
140 debug!("[Boot] [Workspace] Parsed workspace ok. folder_count={}", Folders.len());
141
142 (Folders, Some(Path))
143 })
144 .unwrap_or_else(|Error| {
145 error!("[Boot] [Workspace] Parse failed: {}. Continuing without workspace.", Error);
146
147 (Vec::new(), None)
148 })
149 },
150 Err(Error) => {
151 error!("[Boot] [Workspace] Read failed: {}. Continuing without workspace.", Error);
152
153 (Vec::new(), None)
154 },
155 }
156 } else {
157 debug!("[Boot] [Workspace] No workspace provided. Starting empty.");
158
159 (Vec::new(), None)
160 };
161
162 debug!("[Boot] [State] Building ApplicationState...");
166
167 let AppState = Arc::new(ApplicationState {
168 WorkSpaceFolders:Arc::new(Mutex::new(InitialFolders)),
169 WorkSpaceConfigurationPath:Arc::new(Mutex::new(WorkSpaceConfigurationPath)),
170 ..ApplicationState::default()
171 });
172
173 debug!("[Boot] [State] ApplicationState created and managed.");
174
175 let NumberOfWorkers = num_cpus::get().max(2);
179
180 debug!("[Boot] [Echo] Creating scheduler. workers={}", NumberOfWorkers);
181
182 let Scheduler = SchedulerBuilder::Create().WithWorkerCount(NumberOfWorkers).Build();
183
184 debug!("[Boot] [Echo] Scheduler built.");
185
186 let SchedulerForShutdown = Arc::new(Scheduler);
187
188 let SchedulerForRunTime = SchedulerForShutdown.clone();
189
190 TraceStep!("[Boot] [Echo] Scheduler handles prepared.");
191
192 debug!("[Boot] [Localhost] Selecting unused port...");
196
197 let ServerPort =
198 portpicker::pick_unused_port().expect("FATAL: Failed to find a free port for Localhost Server");
199
200 debug!("[Boot] [Localhost] Selected port={}", ServerPort);
201
202 let LocalhostUrl = format!("http://localhost:{}", ServerPort);
203
204 println!("[Boot] [Localhost] Selected: {} ({})", ServerPort, LocalhostUrl);
205
206 let EnvLogLevel = std::env::var("RUST_LOG").ok().and_then(|s| s.parse::<LevelFilter>().ok());
217
218 let DefaultLogLevel = if cfg!(debug_assertions) { LevelFilter::Debug } else { LevelFilter::Info };
219
220 let LogLevel = EnvLogLevel.unwrap_or(DefaultLogLevel);
221
222 eprintln!(
225 "[Boot] [Logging] Resolved LogLevel={:?} (env={:?}, default={:?})",
226 LogLevel, EnvLogLevel, DefaultLogLevel
227 );
228
229 #[allow(unused_mut)]
233 let mut Builder = tauri::Builder::default();
234
235 #[cfg(any(windows, target_os = "linux"))]
236 {
237 Builder = Builder.any_thread();
238 }
239
240 Builder
241 .plugin(
245 tauri_plugin_log::Builder::new()
246 .targets([
247 Target::new(TargetKind::Stdout),
248 Target::new(TargetKind::LogDir {
249 file_name: Some("Mountain.log".into()),
250 }),
251 Target::new(TargetKind::Webview),
252 ])
253 .timezone_strategy(TimezoneStrategy::UseLocal)
254 .rotation_strategy(RotationStrategy::KeepAll)
255 .level(LogLevel)
256 .level_for("hyper", LevelFilter::Info)
258 .level_for("mio", LevelFilter::Info)
259 .level_for("tao", LevelFilter::Info)
260 .level_for("tracing", LevelFilter::Info)
261 .filter(|Metadata| {
263 !Metadata.target().starts_with("polling")
264 && !Metadata.target().starts_with("tokio_reactor")
265 && !Metadata.target().starts_with("want")
266 })
267 .format(|out, message, record| {
269 out.finish(format_args!(
270 "[{:<5}] [{}] {}",
271 record.level(),
272 record.target(),
273 message
274 ))
275 })
276 .build(),
277 )
278 .plugin(tauri_plugin_localhost::Builder::new(ServerPort)
282 .on_request(|_, Response| {
283 Response.add_header("Access-Control-Allow-Origin", "*");
284 Response.add_header("Access-Control-Allow-Methods", "GET, POST, OPTIONS, HEAD");
285 Response.add_header("Access-Control-Allow-Headers", "Content-Type, Authorization, Origin, Accept");
286 })
287 .build())
288 .manage(AppState.clone())
292 .setup({
296 let LocalhostUrl = LocalhostUrl.clone();
297
298 move |Application| {
299 info!("[Lifecycle] [Setup] Setup hook started.");
300
301 debug!("[Lifecycle] [Setup] LocalhostUrl={}", LocalhostUrl);
302
303 let ApplicationHandle = Application.handle().clone();
304
305 TraceStep!("[Lifecycle] [Setup] AppHandle acquired.");
306
307 debug!("[Lifecycle] [Commands] Registering native commands...");
311
312 Command::Bootstrap::RegisterNativeCommands(&ApplicationHandle, &AppState)
313 .expect("FATAL: Failed to register native commands.");
314
315 debug!("[Lifecycle] [Commands] Native commands registered.");
316
317 debug!("[UI] [Window] Building init script...");
321
322 let InitScript = format!("window.__MOUNTAIN_BASE_URL__ = '{}';", LocalhostUrl);
323
324 TraceStep!("[UI] [Window] InitScript bytes={}", InitScript.len());
325
326 debug!("[UI] [Window] Creating window builder...");
327
328 let mut WindowBuilder = tauri::WebviewWindowBuilder::new(
329 Application,
330 "main",
331 tauri::WebviewUrl::External(
332 format!("{}/Application/index.html", LocalhostUrl).parse().unwrap(),
333 ),
334 )
335 .use_https_scheme(false)
336 .initialization_script(&InitScript)
337 .zoom_hotkeys_enabled(true)
338 .browser_extensions_enabled(false);
339
340 #[cfg(any(target_os = "windows", target_os = "macos", target_os = "linux"))]
341 {
342 WindowBuilder = WindowBuilder
343 .title("Mountain")
344 .maximized(true)
345 .decorations(false)
346 .shadow(true);
347 }
348
349 debug!("[UI] [Window] Building main window...");
350
351 #[allow(unused_variables)]
352 let MainWindow = WindowBuilder.build().expect("FATAL: Main window build failed");
353
354 info!("[UI] [Window] Main window ready.");
355
356 #[cfg(debug_assertions)]
357 {
358 debug!("[UI] [Window] Debug build: opening DevTools.");
359
360 MainWindow.open_devtools();
361 }
362
363 debug!("[Backend] [Env] Creating MountainEnvironment...");
367
368 let Environment = Arc::new(MountainEnvironment::Create(ApplicationHandle.clone()));
369
370 info!("[Backend] [Env] MountainEnvironment ready.");
371
372 debug!("[Backend] [Runtime] Creating ApplicationRunTime...");
376
377 let RunTime = Arc::new(ApplicationRunTime::Create(
378 SchedulerForRunTime.clone(),
379 Environment.clone(),
380 ));
381
382 ApplicationHandle.manage(RunTime);
383
384 info!("[Backend] [Runtime] ApplicationRunTime managed.");
385
386 let PostSetupApplicationHandle = ApplicationHandle.clone();
390
391 let PostSetupEnvironment = Environment.clone();
392
393 tauri::async_runtime::spawn(async move {
394 info!("[Lifecycle] [PostSetup] Starting...");
395
396 let AppStateForSetup = PostSetupEnvironment.ApplicationState.clone();
397
398 TraceStep!("[Lifecycle] [PostSetup] AppState cloned.");
399
400 debug!("[Config] InitializeAndMergeConfigurations starting...");
402
403 if let Err(Error) = InitializeAndMergeConfigurations(&PostSetupEnvironment).await {
404 error!("[Config] InitializeAndMergeConfigurations failed: {}", Error);
405 } else {
406 info!("[Config] InitializeAndMergeConfigurations done.");
407 }
408
409 {
411 debug!("[Extensions] [ScanPaths] Locking ExtensionScanPaths...");
412
413 let mut ScanPathsGuard = AppStateForSetup
414 .ExtensionScanPaths
415 .lock()
416 .map_err(MapLockError)
417 .expect("FATAL: Failed to lock ExtensionScanPaths");
418
419 debug!("[Extensions] [ScanPaths] Adding default scan paths...");
420
421 if let Ok(ExecutableDirectory) = std::env::current_exe() {
422 if let Some(Parent) = ExecutableDirectory.parent() {
423 let ResourcesPath = Parent.join("../Resources/extensions");
424
425 let LocalPath = Parent.join("extensions");
426
427 debug!(
428 "[Extensions] [ScanPaths] + {}",
429 ResourcesPath.display()
430 );
431
432 ScanPathsGuard.push(ResourcesPath);
433
434 debug!(
435 "[Extensions] [ScanPaths] + {}",
436 LocalPath.display()
437 );
438
439 ScanPathsGuard.push(LocalPath);
440 }
441 }
442
443 info!(
444 "[Extensions] [ScanPaths] Initialized: {:?}",
445 *ScanPathsGuard
446 );
447 }
448
449 debug!("[Extensions] [Scan] ScanAndPopulateExtensions starting...");
451
452 if let Err(Error) =
453 ScanAndPopulateExtensions(PostSetupApplicationHandle.clone(), &AppStateForSetup).await
454 {
455 error!("[Extensions] [Scan] Failed: {}", Error);
456 } else {
457 info!("[Extensions] [Scan] Completed.");
458 }
459
460 debug!("[Vine] [Init] Starting Vine gRPC server...");
462
463 if let Err(Error) = Vine::Server::Initialize::Initialize(
464 PostSetupApplicationHandle.clone(),
465 "[::1]:50051".to_string(),
466 ) {
467 error!("[Vine] [Init] Failed: {}", Error);
468 } else {
469 info!("[Vine] [Init] Ready.");
470 }
471
472 debug!("[Cocoon] [Init] InitializeCocoon starting...");
474
475 if let Err(Error) = InitializeCocoon(&PostSetupApplicationHandle, &PostSetupEnvironment).await {
476 error!("[Cocoon] [Init] Failed: {}", Error);
477 } else {
478 info!("[Cocoon] [Init] Ready.");
479 }
480
481 info!("[Lifecycle] [PostSetup] Complete. System ready.");
482 });
483
484 Ok(())
485 }
486 })
487 .plugin(tauri_plugin_dialog::init())
491 .plugin(tauri_plugin_fs::init())
492 .invoke_handler(tauri::generate_handler![
496 MountainGetWorkbenchConfiguration,
497 Command::TreeView::GetTreeViewChildren,
498 Command::LanguageFeature::MountainProvideHover,
499 Command::LanguageFeature::MountainProvideCompletions,
500 Command::LanguageFeature::MountainProvideDefinition,
501 Command::LanguageFeature::MountainProvideReferences,
502 Command::SourceControlManagement::GetAllSourceControlManagementState,
503 Command::Keybinding::GetResolvedKeybinding,
504 crate::Track::DispatchLogic::DispatchFrontendCommand,
505 crate::Track::DispatchLogic::ResolveUIRequest,
506 ])
507 .build(tauri::generate_context!())
511 .expect("FATAL: Error while building Mountain Tauri application")
512 .run(move |ApplicationHandle, Event| {
513 if cfg!(debug_assertions) {
515 match &Event {
516 RunEvent::MainEventsCleared => {},
517 RunEvent::WindowEvent { .. } => {},
518 _ => debug!("[Lifecycle] [RunEvent] {:?}", Event),
519 }
520 }
521
522 if let RunEvent::ExitRequested { api, .. } = Event {
523 warn!("[Lifecycle] [Shutdown] Exit requested. Starting graceful shutdown...");
524
525 api.prevent_exit();
526
527 let SchedulerHandle = SchedulerForShutdown.clone();
528
529 let ApplicationHandleClone = ApplicationHandle.clone();
530
531 tokio::spawn(async move {
532 debug!("[Lifecycle] [Shutdown] Shutting down ApplicationRunTime...");
533
534 if let Some(RunTime) = ApplicationHandleClone.try_state::<Arc<ApplicationRunTime>>() {
535 RunTime.inner().clone().Shutdown().await;
536
537 info!("[Lifecycle] [Shutdown] ApplicationRunTime stopped.");
538
539 } else {
540 error!("[Lifecycle] [Shutdown] ApplicationRunTime not found.");
541
542 }
543
544 debug!("[Lifecycle] [Shutdown] Stopping Echo scheduler...");
545
546 if let Ok(mut Scheduler) = Arc::try_unwrap(SchedulerHandle) {
547 Scheduler.Stop().await;
548
549 info!("[Lifecycle] [Shutdown] Echo scheduler stopped.");
550 } else {
551 error!("[Lifecycle] [Shutdown] Scheduler not exclusively owned; cannot stop cleanly.");
552 }
553
554 info!("[Lifecycle] [Shutdown] Done. Exiting process.");
555
556 ApplicationHandleClone.exit(0);
557 });
558 }
559 });
560
561 info!("[Lifecycle] [Exit] Mountain application has shut down.");
562 });
563}