Mountain/IPC/
WindServiceAdapters.rs

1//! # Wind Service Adapters - Type Conversion & Service Bridging
2//!
3//! **File Responsibilities:**
4//! This module provides the adapter layer that handles type conversion and
5//! service abstraction between Wind's TypeScript interfaces and Mountain's Rust
6//! implementations. It allows Mountain services to present Wind-compatible APIs
7//! while using Mountain's internal architecture.
8//!
9//! **Architectural Role in Wind-Mountain Connection:**
10//!
11//! The WindServiceAdapters module serves as the translation layer that:
12//!
13//! 1. **Converts Data Types:** Transforms TypeScript types to Rust types and
14//!    vice versa
15//! 2. **Abstracts Services:** Provides Wind-compatible service interfaces over
16//!    Mountain services
17//! 3. **Handles Configuration:** Converts between different configuration
18//!    formats
19//! 4. **Maintains Compatibility:** Ensures Wind contracts are satisfied
20//!
21//! **Adapter Pattern:**
22//!
23//! This module implements the Adapter design pattern to bridge the interface
24//! gap:
25//!
26//! ```
27//! Wind's IFileService (TypeScript interface)
28//!        |
29//!        |  Expected interface
30//!        v
31//! WindFileService (Rust adapter)
32//!        |
33//!        |  Delegates to
34//!        v
35//! Mountain's FileSystemReader (Rust trait)
36//! ```
37//!
38//! **Key Structures:**
39//!
40//! **1. WindDesktopConfiguration:**
41//! - Represents Wind's complete desktop configuration structure
42//! - Mirrors Wind's TypeScript interface
43//! - Includes window settings, paths, platform info, profile data
44//!
45//! **2. WindServiceAdapter:**
46//! Main adapter that converts between Mountain and Wind formats
47//! - `convert_to_wind_configuration()` - Mountain config to Wind config
48//! - `get_environment_service()` - Wind-compatible environment service
49//! - `get_file_service()` - Wind-compatible file service
50//! - `get_storage_service()` - Wind-compatible storage service
51//! - `get_configuration_service()` - Wind-compatible configuration service
52//!
53//! **3. Individual Service Adapters:**
54//!
55//! **WindEnvironmentService:**
56//! Provides Wind-compatible environment variable access
57//! - `get_app_root()` - Get application root path
58//! - `get_user_data_path()` - Get user data directory
59//!
60//! **WindFileService:**
61//! Adapts Mountain's file system to Wind's interface
62//! - `read_file()` - Read file as bytes
63//! - `write_file()` - Write file from bytes
64//! - `stat_file()` - Get file metadata as JSON
65//!
66//! **WindStorageService:**
67//! Adapts Mountain's storage to Wind's interface
68//! - `get()` - Get storage value as JSON
69//! - `set()` - Set storage value from JSON
70//!
71//! **WindConfigurationService:**
72//! Adapts Mountain's configuration to Wind's interface
73//! - `get_value()` - Get configuration value
74//! - `update_value()` - Update configuration value
75//!
76//! **Type Conversion Examples:**
77//!
78//! **Configuration Conversion:**
79//! ```typescript
80//! // Wind TypeScript Configuration
81//! interface IDesktopConfiguration {
82//!   windowId: number;
83//!   appRoot: string;
84//!   userDataPath: string;
85//!   // ... more fields
86//! }
87//! ```
88//!
89//! ```text
90//! // Mountain Rust Configuration (after conversion)
91//! struct WindDesktopConfiguration {
92//! pub window_id:u32,
93//! pub app_root:String,
94//! pub user_data_path:String,
95//! // ... more fields
96//! }
97//! ```
98//!
99//! **File Service Integration:**
100//!
101//! Mountain's file system uses traits for abstraction:
102//!
103//! ```text
104//! let reader:Arc<dyn FileSystemReader> = runtime.Environment.Require();
105//! let writer:Arc<dyn FileSystemWriter> = runtime.Environment.Require();
106//!
107//! // Adapt to Wind's interface
108//! let wind_file_service = WindFileService::new(reader, writer);
109//! let bytes = wind_file_service.read_file(path).await?;
110//! ```
111//!
112//! **Configuration Bridge Integration:**
113//!
114//! The WindServiceAdapter works closely with ConfigurationBridge:
115//! - ConfigurationBridge uses WindServiceAdapter to convert formats
116//! - WindServiceAdapter maintains type compatibility
117//! - Both work together to ensure seamless Wind-Mountain integration
118//!
119//! **Error Handling:**
120//!
121//! All adapter methods return `Result<T, String>` with descriptive errors:
122//! - Type conversion errors include the field and reason
123//! - Service delegation errors propagate with context
124//! - All errors are in a format Wind can understand
125//!
126//! **Usage Pattern:**
127//!
128//! ```text
129//! // Create adapter
130//! let adapter = WindServiceAdapter::new(runtime);
131//!
132//! // Get Mountain config
133//! let mountain_config = get_mountain_config().await?;
134//!
135//! // Convert to Wind format
136//! let wind_config = adapter.convert_to_wind_configuration(mountain_config).await?;
137//!
138//! // Get Wind-compatible services
139//! let file_service = adapter.get_file_service().await?;
140//! let config_service = adapter.get_configuration_service().await?;
141//! ```
142
143use std::{path::PathBuf, sync::Arc};
144
145use log::{debug, info};
146use serde::{Deserialize, Serialize};
147use serde_json::json;
148use url::Url;
149use CommonLibrary::{
150	Configuration::{
151		ConfigurationProvider::ConfigurationProvider,
152		DTO::{
153			ConfigurationOverridesDTO as ConfigurationOverridesDTOModule,
154			ConfigurationTarget as ConfigurationTargetModule,
155		},
156	},
157	Environment::Requires::Requires,
158	Error::CommonError::CommonError,
159	FileSystem::{FileSystemReader::FileSystemReader, FileSystemWriter::FileSystemWriter},
160	Storage::StorageProvider::StorageProvider,
161};
162
163// Type aliases for Configuration DTOs to simplify usage
164type ConfigurationOverridesDTO = ConfigurationOverridesDTOModule::ConfigurationOverridesDTO;
165type ConfigurationTarget = ConfigurationTargetModule::ConfigurationTarget;
166
167use crate::RunTime::ApplicationRunTime::ApplicationRunTime;
168
169/// Wind desktop configuration structure
170/// Mirrors Wind's IDesktopConfiguration interface
171#[derive(Debug, Clone, Serialize, Deserialize)]
172pub struct WindDesktopConfiguration {
173	pub window_id:u32,
174	pub app_root:String,
175	pub user_data_path:String,
176	pub temp_path:String,
177	pub log_level:String,
178	pub is_packaged:bool,
179	pub tauri_version:String,
180	pub platform:String,
181	pub arch:String,
182	pub workspace:Option<serde_json::Value>,
183	pub files_to_open_or_create:Option<Vec<FileToOpenOrCreate>>,
184	pub files_to_diff:Option<Vec<FileToDiff>>,
185	pub files_to_wait:Option<FilesToWait>,
186	pub fullscreen:Option<bool>,
187	pub zoom_level:Option<f64>,
188	pub is_custom_zoom_level:Option<bool>,
189	pub profiles:Profiles,
190	pub policies_data:Option<serde_json::Value>,
191	pub loggers:Vec<Logger>,
192	pub backup_path:Option<String>,
193	pub disable_layout_restore:Option<bool>,
194	pub os:OsInfo,
195}
196
197/// File to open or create structure
198#[derive(Debug, Clone, Serialize, Deserialize)]
199pub struct FileToOpenOrCreate {
200	pub file_uri:String,
201}
202
203/// File to diff structure
204#[derive(Debug, Clone, Serialize, Deserialize)]
205pub struct FileToDiff {
206	pub file_uri:String,
207}
208
209/// Files to wait structure
210#[derive(Debug, Clone, Serialize, Deserialize)]
211pub struct FilesToWait {
212	pub wait_marker_file_uri:String,
213	pub paths:Vec<FileToOpenOrCreate>,
214}
215
216/// Profiles structure
217#[derive(Debug, Clone, Serialize, Deserialize)]
218pub struct Profiles {
219	pub all:Vec<serde_json::Value>,
220	pub home:String,
221	pub profile:serde_json::Value,
222}
223
224/// Logger structure
225#[derive(Debug, Clone, Serialize, Deserialize)]
226pub struct Logger {
227	pub resource:serde_json::Value,
228}
229
230/// OS information structure
231#[derive(Debug, Clone, Serialize, Deserialize)]
232pub struct OsInfo {
233	pub release:String,
234}
235
236/// Wind service adapter that bridges Mountain services to Wind's interfaces
237pub struct WindServiceAdapter {
238	runtime:Arc<ApplicationRunTime>,
239}
240
241impl WindServiceAdapter {
242	/// Create a new Wind service adapter
243	pub fn new(runtime:Arc<ApplicationRunTime>) -> Self {
244		info!("[WindServiceAdapters] Creating Wind service adapter");
245		Self { runtime }
246	}
247
248	/// Convert Mountain's sandbox configuration to Wind's desktop configuration
249	pub async fn convert_to_wind_configuration(
250		&self,
251		mountain_config:serde_json::Value,
252	) -> Result<WindDesktopConfiguration, String> {
253		debug!("[WindServiceAdapters] Converting Mountain config to Wind config");
254
255		// Parse the Mountain configuration
256		let config:MountainSandboxConfiguration = serde_json::from_value(mountain_config)
257			.map_err(|e| format!("Failed to parse Mountain configuration: {}", e))?;
258
259		// Convert to Wind's format
260		let wind_config = WindDesktopConfiguration {
261			window_id:config.window_id.parse().unwrap_or(1),
262			app_root:config.app_root,
263			user_data_path:config.user_data_dir,
264			temp_path:config.tmp_dir,
265			log_level:config.log_level.to_string(),
266			is_packaged:config.product_configuration.is_packaged,
267			tauri_version:config.versions.mountain,
268			platform:config.platform,
269			arch:config.arch,
270			workspace:None,
271			files_to_open_or_create:None,
272			files_to_diff:None,
273			files_to_wait:None,
274			fullscreen:Some(false),
275			zoom_level:Some(config.zoom_level),
276			is_custom_zoom_level:Some(false),
277			profiles:Profiles { all:vec![], home:config.home_dir, profile:serde_json::Value::Null },
278			policies_data:None,
279			loggers:vec![],
280			backup_path:Some(config.backup_path),
281			disable_layout_restore:Some(false),
282			os:OsInfo { release:std::env::consts::OS.to_string() },
283		};
284
285		Ok(wind_config)
286	}
287
288	/// Get Wind-compatible environment service
289	pub async fn get_environment_service(&self) -> Result<WindEnvironmentService, String> {
290		debug!("[WindServiceAdapters] Getting Wind environment service");
291
292		Ok(WindEnvironmentService::new())
293	}
294
295	/// Get Wind-compatible file service
296	pub async fn get_file_service(&self) -> Result<WindFileService, String> {
297		debug!("[WindServiceAdapters] Getting Wind file service");
298
299		let file_system_reader:Arc<dyn FileSystemReader> = self.runtime.Environment.Require();
300
301		let file_system_writer:Arc<dyn FileSystemWriter> = self.runtime.Environment.Require();
302
303		Ok(WindFileService::new(file_system_reader, file_system_writer))
304	}
305
306	/// Get Wind-compatible storage service
307	pub async fn get_storage_service(&self) -> Result<WindStorageService, String> {
308		debug!("[WindServiceAdapters] Getting Wind storage service");
309
310		let storage:Arc<dyn StorageProvider> = self.runtime.Environment.Require();
311
312		Ok(WindStorageService::new(storage))
313	}
314
315	/// Get Wind-compatible configuration service
316	pub async fn get_configuration_service(&self) -> Result<WindConfigurationService, String> {
317		debug!("[WindServiceAdapters] Getting Wind configuration service");
318
319		let config:Arc<dyn ConfigurationProvider> = self.runtime.Environment.Require();
320
321		Ok(WindConfigurationService::new(config))
322	}
323}
324
325/// Wind environment service adapter
326pub struct WindEnvironmentService {
327	// Environment variables are accessed via std::env
328}
329
330impl WindEnvironmentService {
331	pub fn new() -> Self { Self {} }
332
333	pub async fn get_app_root(&self) -> Result<String, String> { std::env::var("APP_ROOT").map_err(|e| e.to_string()) }
334
335	pub async fn get_user_data_path(&self) -> Result<String, String> {
336		std::env::var("USER_DATA_PATH").map_err(|e| e.to_string())
337	}
338}
339
340/// Wind file service adapter
341pub struct WindFileService {
342	reader:Arc<dyn FileSystemReader>,
343	writer:Arc<dyn FileSystemWriter>,
344}
345
346impl WindFileService {
347	pub fn new(reader:Arc<dyn FileSystemReader>, writer:Arc<dyn FileSystemWriter>) -> Self { Self { reader, writer } }
348
349	pub async fn read_file(&self, path:String) -> Result<Vec<u8>, String> {
350		self.reader.ReadFile(&PathBuf::from(path)).await.map_err(|e| e.to_string())
351	}
352
353	pub async fn write_file(&self, path:String, content:Vec<u8>) -> Result<(), String> {
354		self.writer
355			.WriteFile(&PathBuf::from(path), content, true, true)
356			.await
357			.map_err(|e:CommonError| e.to_string())
358	}
359
360	pub async fn stat_file(&self, path:String) -> Result<serde_json::Value, String> {
361		let stat_dto = self
362			.reader
363			.StatFile(&PathBuf::from(path))
364			.await
365			.map_err(|e:CommonError| e.to_string())?;
366		Ok(json!(stat_dto))
367	}
368}
369
370/// Wind storage service adapter
371pub struct WindStorageService {
372	provider:Arc<dyn StorageProvider>,
373}
374
375impl WindStorageService {
376	pub fn new(provider:Arc<dyn StorageProvider>) -> Self { Self { provider } }
377
378	pub async fn get(&self, key:String) -> Result<serde_json::Value, String> {
379		let value = self
380			.provider
381			.GetStorageValue(false, &key)
382			.await
383			.map_err(|e:CommonError| e.to_string())?
384			.ok_or_else(|| "Storage key not found".to_string())?;
385		Ok(value)
386	}
387
388	pub async fn set(&self, key:String, value:serde_json::Value) -> Result<(), String> {
389		self.provider
390			.UpdateStorageValue(false, key.to_string(), Some(value))
391			.await
392			.map_err(|e:CommonError| e.to_string())
393	}
394}
395
396/// Wind configuration service adapter
397pub struct WindConfigurationService {
398	provider:Arc<dyn ConfigurationProvider>,
399}
400
401impl WindConfigurationService {
402	pub fn new(provider:Arc<dyn ConfigurationProvider>) -> Self { Self { provider } }
403
404	pub async fn get_value(&self, key:String) -> Result<serde_json::Value, String> {
405		self.provider
406			.GetConfigurationValue(Some(key.to_string()), ConfigurationOverridesDTO::default())
407			.await
408			.map_err(|e| e.to_string())
409	}
410
411	pub async fn update_value(&self, key:String, value:serde_json::Value) -> Result<(), String> {
412		self.provider
413			.UpdateConfigurationValue(
414				key,
415				value,
416				ConfigurationTarget::User,
417				ConfigurationOverridesDTO::default(),
418				None,
419			)
420			.await
421			.map_err(|e| e.to_string())
422	}
423}
424
425/// Mountain sandbox configuration structure
426#[derive(Debug, Clone, Serialize, Deserialize)]
427struct MountainSandboxConfiguration {
428	pub window_id:String,
429	pub machine_id:String,
430	pub session_id:String,
431	pub log_level:i32,
432	pub user_env:std::collections::HashMap<String, String>,
433	pub app_root:String,
434	pub app_name:String,
435	pub app_uri_scheme:String,
436	pub app_language:String,
437	pub app_host:String,
438	pub platform:String,
439	pub arch:String,
440	pub versions:Versions,
441	pub exec_path:String,
442	pub home_dir:String,
443	pub tmp_dir:String,
444	pub user_data_dir:String,
445	pub backup_path:String,
446	pub resources_path:String,
447	pub vscode_cwd:String,
448	pub nls:NLSConfiguration,
449	pub product_configuration:ProductConfiguration,
450	pub zoom_level:f64,
451}
452
453#[derive(Debug, Clone, Serialize, Deserialize)]
454struct Versions {
455	pub mountain:String,
456	pub electron:String,
457	pub chrome:String,
458	pub node:String,
459}
460
461#[derive(Debug, Clone, Serialize, Deserialize)]
462struct NLSConfiguration {
463	pub messages:std::collections::HashMap<String, String>,
464	pub language:String,
465	pub available_languages:std::collections::HashMap<String, String>,
466}
467
468#[derive(Debug, Clone, Serialize, Deserialize)]
469struct ProductConfiguration {
470	pub name_short:String,
471	pub name_long:String,
472	pub application_name:String,
473	pub embedder_identifier:String,
474	pub is_packaged:bool,
475}