1use std::{path::PathBuf, sync::Arc};
171
172use log::{debug, error, info};
173use serde_json::{Value, json};
174use tauri::{AppHandle, Manager, command};
175use CommonLibrary::Configuration::DTO::{
177 ConfigurationOverridesDTO as ConfigurationOverridesDTOModule,
178 ConfigurationTarget as ConfigurationTargetModule,
179};
180type ConfigurationOverridesDTO = ConfigurationOverridesDTOModule::ConfigurationOverridesDTO;
181type ConfigurationTarget = ConfigurationTargetModule::ConfigurationTarget;
182
183use CommonLibrary::{
184 Configuration::ConfigurationProvider::ConfigurationProvider,
185 Environment::Requires::Requires,
186 Error::CommonError::CommonError,
187 FileSystem::{FileSystemReader::FileSystemReader, FileSystemWriter::FileSystemWriter},
188 Storage::StorageProvider::StorageProvider,
189};
190
191use crate::{ApplicationState::ApplicationState, RunTime::ApplicationRunTime::ApplicationRunTime};
192
193#[tauri::command]
196pub async fn mountain_ipc_invoke(app_handle:AppHandle, command:String, args:Vec<Value>) -> Result<Value, String> {
197 debug!("[WindServiceHandlers] IPC Invoke command: {}, args: {:?}", command, args);
198
199 let runtime = app_handle.state::<Arc<ApplicationRunTime>>();
201
202 match command.as_str() {
204 "configuration:get" => handle_configuration_get(runtime.inner().clone(), args).await,
206 "configuration:update" => handle_configuration_update(runtime.inner().clone(), args).await,
207
208 "file:read" => handle_file_read(runtime.inner().clone(), args).await,
210 "file:write" => handle_file_write(runtime.inner().clone(), args).await,
211 "file:stat" => handle_file_stat(runtime.inner().clone(), args).await,
212 "file:exists" => handle_file_exists(runtime.inner().clone(), args).await,
213 "file:delete" => handle_file_delete(runtime.inner().clone(), args).await,
214 "file:copy" => handle_file_copy(runtime.inner().clone(), args).await,
215 "file:move" => handle_file_move(runtime.inner().clone(), args).await,
216 "file:mkdir" => handle_file_mkdir(runtime.inner().clone(), args).await,
217 "file:readdir" => handle_file_readdir(runtime.inner().clone(), args).await,
218 "file:readBinary" => handle_file_read_binary(runtime.inner().clone(), args).await,
219 "file:writeBinary" => handle_file_write_binary(runtime.inner().clone(), args).await,
220
221 "storage:get" => handle_storage_get(runtime.inner().clone(), args).await,
223 "storage:set" => handle_storage_set(runtime.inner().clone(), args).await,
224
225 "environment:get" => handle_environment_get(runtime.inner().clone(), args).await,
227
228 "native:showItemInFolder" => handle_show_item_in_folder(runtime.inner().clone(), args).await,
230 "native:openExternal" => handle_open_external(runtime.inner().clone(), args).await,
231
232 "workbench:getConfiguration" => handle_workbench_configuration(runtime.inner().clone(), args).await,
234
235 "mountain_get_status" => {
237 let status = json!({
238 "connected": true,
239 "version": "1.0.0"
240 });
241 Ok(status)
242 },
243 "mountain_get_configuration" => {
244 let config = json!({
245 "editor": { "theme": "dark" },
246 "extensions": { "installed": [] }
247 });
248 Ok(config)
249 },
250 "mountain_get_services_status" => {
251 let services = json!({
252 "editor": { "status": "running" },
253 "extensionHost": { "status": "running" }
254 });
255 Ok(services)
256 },
257 "mountain_get_state" => {
258 let state = json!({
259 "ui": {},
260 "editor": {},
261 "workspace": {}
262 });
263 Ok(state)
264 },
265
266 _ => {
268 error!("[WindServiceHandlers] Unknown IPC command: {}", command);
269 Err(format!("Unknown IPC command: {}", command))
270 },
271 }
272}
273
274async fn handle_configuration_get(runtime:Arc<ApplicationRunTime>, args:Vec<Value>) -> Result<Value, String> {
276 let key = args
277 .get(0)
278 .ok_or("Missing configuration key".to_string())?
279 .as_str()
280 .ok_or("Configuration key must be a string".to_string())?;
281
282 let provider:Arc<dyn ConfigurationProvider> = runtime.Environment.Require();
284
285 let value = provider
286 .GetConfigurationValue(Some(key.to_string()), ConfigurationOverridesDTO::default())
287 .await
288 .map_err(|e| format!("Failed to get configuration: {}", e))?;
289
290 debug!("[WindServiceHandlers] Configuration get: {} = {:?}", key, value);
291 Ok(value)
292}
293
294async fn handle_configuration_update(runtime:Arc<ApplicationRunTime>, args:Vec<Value>) -> Result<Value, String> {
296 let key = args
297 .get(0)
298 .ok_or("Missing configuration key".to_string())?
299 .as_str()
300 .ok_or("Configuration key must be a string".to_string())?;
301
302 let value = args.get(1).ok_or("Missing configuration value".to_string())?.clone();
303
304 let provider:Arc<dyn ConfigurationProvider> = runtime.Environment.Require();
306
307 provider
308 .UpdateConfigurationValue(
309 key.to_string(),
310 value,
311 ConfigurationTarget::User,
312 ConfigurationOverridesDTO::default(),
313 None,
314 )
315 .await
316 .map_err(|e| format!("Failed to update configuration: {}", e))?;
317
318 debug!("[WindServiceHandlers] Configuration updated: {}", key);
319 Ok(Value::Null)
320}
321
322async fn handle_file_read(runtime:Arc<ApplicationRunTime>, args:Vec<Value>) -> Result<Value, String> {
324 let path = args
325 .get(0)
326 .ok_or("Missing file path".to_string())?
327 .as_str()
328 .ok_or("File path must be a string".to_string())?;
329
330 let provider:Arc<dyn FileSystemReader> = runtime.Environment.Require();
332
333 let content = provider
334 .ReadFile(&PathBuf::from(path))
335 .await
336 .map_err(|e| format!("Failed to read file: {}", e))?;
337
338 debug!("[WindServiceHandlers] File read: {} ({} bytes)", path, content.len());
339 Ok(json!(content))
340}
341
342async fn handle_file_write(runtime:Arc<ApplicationRunTime>, args:Vec<Value>) -> Result<Value, String> {
344 let path = args
345 .get(0)
346 .ok_or("Missing file path".to_string())?
347 .as_str()
348 .ok_or("File path must be a string".to_string())?;
349
350 let content = args
351 .get(1)
352 .ok_or("Missing file content".to_string())?
353 .as_str()
354 .ok_or("File content must be a string".to_string())?;
355
356 let provider:Arc<dyn FileSystemWriter> = runtime.Environment.Require();
358
359 provider
360 .WriteFile(&PathBuf::from(path), content.as_bytes().to_vec(), true, true)
361 .await
362 .map_err(|e:CommonError| format!("Failed to write file: {}", e))?;
363
364 debug!("[WindServiceHandlers] File written: {} ({} bytes)", path, content.len());
365 Ok(Value::Null)
366}
367
368async fn handle_file_stat(runtime:Arc<ApplicationRunTime>, args:Vec<Value>) -> Result<Value, String> {
370 let path = args
371 .get(0)
372 .ok_or("Missing file path".to_string())?
373 .as_str()
374 .ok_or("File path must be a string".to_string())?;
375
376 let provider:Arc<dyn FileSystemReader> = runtime.Environment.Require();
378
379 let stats = provider
380 .StatFile(&PathBuf::from(path))
381 .await
382 .map_err(|e| format!("Failed to stat file: {}", e))?;
383
384 debug!("[WindServiceHandlers] File stat: {}", path);
385 Ok(json!(stats))
386}
387
388async fn handle_file_exists(runtime:Arc<ApplicationRunTime>, args:Vec<Value>) -> Result<Value, String> {
390 let path = args
391 .get(0)
392 .ok_or("Missing file path".to_string())?
393 .as_str()
394 .ok_or("File path must be a string".to_string())?;
395
396 let provider:Arc<dyn FileSystemReader> = runtime.Environment.Require();
398
399 let exists = provider.StatFile(&PathBuf::from(path)).await.is_ok();
400
401 debug!("[WindServiceHandlers] File exists check: {} = {}", path, exists);
402 Ok(json!(exists))
403}
404
405async fn handle_file_delete(runtime:Arc<ApplicationRunTime>, args:Vec<Value>) -> Result<Value, String> {
407 let path = args
408 .get(0)
409 .ok_or("Missing file path".to_string())?
410 .as_str()
411 .ok_or("File path must be a string".to_string())?;
412
413 let provider:Arc<dyn FileSystemWriter> = runtime.Environment.Require();
415
416 provider
417 .Delete(&PathBuf::from(path), false, false)
418 .await
419 .map_err(|e:CommonError| format!("Failed to delete file: {}", e))?;
420
421 debug!("[WindServiceHandlers] File deleted: {}", path);
422 Ok(Value::Null)
423}
424
425async fn handle_file_copy(runtime:Arc<ApplicationRunTime>, args:Vec<Value>) -> Result<Value, String> {
427 let source = args
428 .get(0)
429 .ok_or("Missing source path".to_string())?
430 .as_str()
431 .ok_or("Source path must be a string".to_string())?;
432
433 let destination = args
434 .get(1)
435 .ok_or("Missing destination path".to_string())?
436 .as_str()
437 .ok_or("Destination path must be a string".to_string())?;
438
439 let provider:Arc<dyn FileSystemWriter> = runtime.Environment.Require();
441
442 provider
443 .Copy(&PathBuf::from(source), &PathBuf::from(destination), false)
444 .await
445 .map_err(|e:CommonError| format!("Failed to copy file: {} -> {}", source, destination))?;
446
447 debug!("[WindServiceHandlers] File copied: {} -> {}", source, destination);
448 Ok(Value::Null)
449}
450
451async fn handle_file_move(runtime:Arc<ApplicationRunTime>, args:Vec<Value>) -> Result<Value, String> {
453 let source = args
454 .get(0)
455 .ok_or("Missing source path".to_string())?
456 .as_str()
457 .ok_or("Source path must be a string".to_string())?;
458
459 let destination = args
460 .get(1)
461 .ok_or("Missing destination path".to_string())?
462 .as_str()
463 .ok_or("Destination path must be a string".to_string())?;
464
465 let provider:Arc<dyn FileSystemWriter> = runtime.Environment.Require();
467
468 provider
469 .Rename(&PathBuf::from(source), &PathBuf::from(destination), false)
470 .await
471 .map_err(|e:CommonError| format!("Failed to move file: {} -> {}", source, destination))?;
472
473 debug!("[WindServiceHandlers] File moved: {} -> {}", source, destination);
474 Ok(Value::Null)
475}
476
477async fn handle_file_mkdir(runtime:Arc<ApplicationRunTime>, args:Vec<Value>) -> Result<Value, String> {
479 let path = args
480 .get(0)
481 .ok_or("Missing directory path".to_string())?
482 .as_str()
483 .ok_or("Directory path must be a string".to_string())?;
484
485 let recursive = args.get(1).and_then(|v| v.as_bool()).unwrap_or(true);
486
487 let provider:Arc<dyn FileSystemWriter> = runtime.Environment.Require();
489
490 provider
491 .CreateDirectory(&PathBuf::from(path), recursive)
492 .await
493 .map_err(|e:CommonError| format!("Failed to create directory: {}", e))?;
494
495 debug!("[WindServiceHandlers] Directory created: {} (recursive: {})", path, recursive);
496 Ok(Value::Null)
497}
498
499async fn handle_file_readdir(runtime:Arc<ApplicationRunTime>, args:Vec<Value>) -> Result<Value, String> {
501 let path = args
502 .get(0)
503 .ok_or("Missing directory path".to_string())?
504 .as_str()
505 .ok_or("Directory path must be a string".to_string())?;
506
507 let provider:Arc<dyn FileSystemReader> = runtime.Environment.Require();
509
510 let entries = provider
511 .ReadDirectory(&PathBuf::from(path))
512 .await
513 .map_err(|e| format!("Failed to read directory: {}", e))?;
514
515 debug!("[WindServiceHandlers] Directory read: {} ({} entries)", path, entries.len());
516 Ok(json!(entries))
517}
518
519async fn handle_file_read_binary(runtime:Arc<ApplicationRunTime>, args:Vec<Value>) -> Result<Value, String> {
521 let path = args
522 .get(0)
523 .ok_or("Missing file path".to_string())?
524 .as_str()
525 .ok_or("File path must be a string".to_string())?;
526
527 let provider:Arc<dyn FileSystemReader> = runtime.Environment.Require();
529
530 let content = provider
531 .ReadFile(&PathBuf::from(path))
532 .await
533 .map_err(|e| format!("Failed to read binary file: {}", e))?;
534
535 debug!("[WindServiceHandlers] Binary file read: {} ({} bytes)", path, content.len());
536 Ok(json!(content))
537}
538
539async fn handle_file_write_binary(runtime:Arc<ApplicationRunTime>, args:Vec<Value>) -> Result<Value, String> {
541 let path = args
542 .get(0)
543 .ok_or("Missing file path".to_string())?
544 .as_str()
545 .ok_or("File path must be a string".to_string())?;
546
547 let content = args
548 .get(1)
549 .ok_or("Missing file content".to_string())?
550 .as_str()
551 .ok_or("File content must be a string".to_string())?;
552
553 let content_bytes = content.as_bytes().to_vec();
555 let content_len = content_bytes.len();
556
557 let provider:Arc<dyn FileSystemWriter> = runtime.Environment.Require();
559
560 provider
561 .WriteFile(&PathBuf::from(path), content_bytes.clone(), true, true)
562 .await
563 .map_err(|e:CommonError| format!("Failed to write binary file: {}", e))?;
564
565 debug!("[WindServiceHandlers] Binary file written: {} ({} bytes)", path, content_len);
566 Ok(Value::Null)
567}
568
569async fn handle_storage_get(runtime:Arc<ApplicationRunTime>, args:Vec<Value>) -> Result<Value, String> {
571 let key = args
572 .get(0)
573 .ok_or("Missing storage key".to_string())?
574 .as_str()
575 .ok_or("Storage key must be a string".to_string())?;
576
577 let provider:Arc<dyn StorageProvider> = runtime.Environment.Require();
579
580 let value = provider
581 .GetStorageValue(false, key)
582 .await
583 .map_err(|e| format!("Failed to get storage item: {}", e))?;
584
585 debug!("[WindServiceHandlers] Storage get: {}", key);
586 Ok(value.unwrap_or(Value::Null))
587}
588
589async fn handle_storage_set(runtime:Arc<ApplicationRunTime>, args:Vec<Value>) -> Result<Value, String> {
591 let key = args
592 .get(0)
593 .ok_or("Missing storage key".to_string())?
594 .as_str()
595 .ok_or("Storage key must be a string".to_string())?;
596
597 let value = args.get(1).ok_or("Missing storage value".to_string())?.clone();
598
599 let provider:Arc<dyn StorageProvider> = runtime.Environment.Require();
601
602 provider
603 .UpdateStorageValue(false, key.to_string(), Some(value))
604 .await
605 .map_err(|e| format!("Failed to set storage item: {}", e))?;
606
607 debug!("[WindServiceHandlers] Storage set: {}", key);
608 Ok(Value::Null)
609}
610
611async fn handle_environment_get(runtime:Arc<ApplicationRunTime>, args:Vec<Value>) -> Result<Value, String> {
613 let key = args
614 .get(0)
615 .ok_or("Missing environment key".to_string())?
616 .as_str()
617 .ok_or("Environment key must be a string".to_string())?;
618
619 let value = std::env::var(key).map_err(|e| format!("Failed to get environment variable: {}", e))?;
621
622 debug!("[WindServiceHandlers] Environment get: {}", key);
623 Ok(json!(value))
624}
625
626async fn handle_show_item_in_folder(runtime:Arc<ApplicationRunTime>, args:Vec<Value>) -> Result<Value, String> {
628 let path_str = args
629 .get(0)
630 .ok_or("Missing file path".to_string())?
631 .as_str()
632 .ok_or("File path must be a string".to_string())?;
633
634 debug!("[WindServiceHandlers] Show item in folder: {}", path_str);
636
637 let path = std::path::PathBuf::from(path_str);
638
639 if !path.exists() {
641 return Err(format!("Path does not exist: {}", path_str));
642 }
643
644 #[cfg(target_os = "macos")]
645 {
646 use std::process::Command;
647
648 let result = Command::new("open")
650 .arg("-R")
651 .arg(&path)
652 .output()
653 .map_err(|e| format!("Failed to execute open command: {}", e))?;
654
655 if !result.status.success() {
656 return Err(format!(
657 "Failed to show item in folder: {}",
658 String::from_utf8_lossy(&result.stderr)
659 ));
660 }
661 }
662
663 #[cfg(target_os = "windows")]
664 {
665 use std::process::Command;
666
667 let result = Command::new("explorer")
669 .arg("/select,")
670 .arg(&path)
671 .output()
672 .map_err(|e| format!("Failed to execute explorer command: {}", e))?;
673
674 if !result.status.success() {
675 return Err(format!(
676 "Failed to show item in folder: {}",
677 String::from_utf8_lossy(&result.stderr)
678 ));
679 }
680 }
681
682 #[cfg(target_os = "linux")]
683 {
684 use std::process::Command;
685
686 let file_managers = ["nautilus", "dolphin", "thunar", "pcmanfm", "nemo"];
688 let mut last_error = String::new();
689
690 for manager in file_managers.iter() {
691 let result = Command::new(manager).arg(&path).output();
692
693 match result {
694 Ok(output) if output.status.success() => {
695 debug!("[WindServiceHandlers] Successfully opened with {}", manager);
696 break;
697 },
698 Err(e) => {
699 last_error = e.to_string();
700 continue;
701 },
702 _ => continue,
703 }
704 }
705
706 if !last_error.is_empty() {
707 return Err(format!("Failed to show item in folder with any file manager: {}", last_error));
708 }
709 }
710
711 info!("[WindServiceHandlers] Successfully showed item in folder: {}", path_str);
712 Ok(Value::Bool(true))
713}
714
715async fn handle_open_external(runtime:Arc<ApplicationRunTime>, args:Vec<Value>) -> Result<Value, String> {
717 let url_str = args
718 .get(0)
719 .ok_or("Missing URL".to_string())?
720 .as_str()
721 .ok_or("URL must be a string".to_string())?;
722
723 debug!("[WindServiceHandlers] Open external: {}", url_str);
725
726 if !url_str.starts_with("http://") && !url_str.starts_with("https://") {
728 return Err(format!("Invalid URL format. Must start with http:// or https://: {}", url_str));
729 }
730
731 #[cfg(target_os = "macos")]
732 {
733 use std::process::Command;
734
735 let result = Command::new("open")
737 .arg(url_str)
738 .output()
739 .map_err(|e| format!("Failed to execute open command: {}", e))?;
740
741 if !result.status.success() {
742 return Err(format!("Failed to open URL: {}", String::from_utf8_lossy(&result.stderr)));
743 }
744 }
745
746 #[cfg(target_os = "windows")]
747 {
748 use std::process::Command;
749
750 let result = Command::new("cmd")
752 .arg("/c")
753 .arg("start")
754 .arg(url_str)
755 .output()
756 .map_err(|e| format!("Failed to execute start command: {}", e))?;
757
758 if !result.status.success() {
759 return Err(format!("Failed to open URL: {}", String::from_utf8_lossy(&result.stderr)));
760 }
761 }
762
763 #[cfg(target_os = "linux")]
764 {
765 use std::process::Command;
766
767 let handlers = ["xdg-open", "gnome-open", "kde-open", "x-www-browser"];
769 let mut last_error = String::new();
770
771 for handler in handlers.iter() {
772 let result = Command::new(handler).arg(url_str).output();
773
774 match result {
775 Ok(output) if output.status.success() => {
776 debug!("[WindServiceHandlers] Successfully opened with {}", handler);
777 break;
778 },
779 Err(e) => {
780 last_error = e.to_string();
781 continue;
782 },
783 _ => continue,
784 }
785 }
786
787 if !last_error.is_empty() {
788 return Err(format!("Failed to open URL with any handler: {}", last_error));
789 }
790 }
791
792 info!("[WindServiceHandlers] Successfully opened external URL: {}", url_str);
793 Ok(Value::Bool(true))
794}
795
796async fn handle_workbench_configuration(runtime:Arc<ApplicationRunTime>, _args:Vec<Value>) -> Result<Value, String> {
798 let provider:Arc<dyn ConfigurationProvider> = runtime.Environment.Require();
800
801 let config = provider
802 .GetConfigurationValue(None, ConfigurationOverridesDTO::default())
803 .await
804 .map_err(|e| format!("Failed to get workbench configuration: {}", e))?;
805
806 debug!("[WindServiceHandlers] Workbench configuration retrieved");
807 Ok(config)
808}
809
810pub fn register_wind_ipc_handlers(app_handle:&tauri::AppHandle) -> Result<(), String> {
812 info!("[WindServiceHandlers] Registering Wind IPC command handlers");
813
814 Ok(())
818}