Skip to main content

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//! ```text
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 serde::{Deserialize, Serialize};
146use serde_json::json;
147// use url::Url; // Temporarily disabled - verify usage
148use CommonLibrary::{
149	Configuration::{
150		ConfigurationProvider::ConfigurationProvider,
151		DTO::{
152			ConfigurationOverridesDTO as ConfigurationOverridesDTOModule,
153			ConfigurationTarget as ConfigurationTargetModule,
154		},
155	},
156	Environment::Requires::Requires,
157	Error::CommonError::CommonError,
158	FileSystem::{FileSystemReader::FileSystemReader, FileSystemWriter::FileSystemWriter},
159	Storage::StorageProvider::StorageProvider,
160};
161
162// Type aliases for Configuration DTOs to simplify usage
163type ConfigurationOverridesDTO = ConfigurationOverridesDTOModule::ConfigurationOverridesDTO;
164type ConfigurationTarget = ConfigurationTargetModule::ConfigurationTarget;
165
166use crate::{RunTime::ApplicationRunTime::ApplicationRunTime, dev_log};
167
168/// Wind desktop configuration structure
169/// Mirrors Wind's IDesktopConfiguration interface
170#[derive(Debug, Clone, Serialize, Deserialize)]
171pub struct WindDesktopConfiguration {
172	pub window_id:u32,
173	pub app_root:String,
174	pub user_data_path:String,
175	pub temp_path:String,
176	pub log_level:String,
177	pub is_packaged:bool,
178	pub tauri_version:String,
179	pub platform:String,
180	pub arch:String,
181	pub workspace:Option<serde_json::Value>,
182	pub files_to_open_or_create:Option<Vec<FileToOpenOrCreate>>,
183	pub files_to_diff:Option<Vec<FileToDiff>>,
184	pub files_to_wait:Option<FilesToWait>,
185	pub fullscreen:Option<bool>,
186	pub zoom_level:Option<f64>,
187	pub is_custom_zoom_level:Option<bool>,
188	pub profiles:Profiles,
189	pub policies_data:Option<serde_json::Value>,
190	pub loggers:Vec<Logger>,
191	pub backup_path:Option<String>,
192	pub disable_layout_restore:Option<bool>,
193	pub os:OsInfo,
194}
195
196/// File to open or create structure
197#[derive(Debug, Clone, Serialize, Deserialize)]
198pub struct FileToOpenOrCreate {
199	pub file_uri:String,
200}
201
202/// File to diff structure
203#[derive(Debug, Clone, Serialize, Deserialize)]
204pub struct FileToDiff {
205	pub file_uri:String,
206}
207
208/// Files to wait structure
209#[derive(Debug, Clone, Serialize, Deserialize)]
210pub struct FilesToWait {
211	pub wait_marker_file_uri:String,
212	pub paths:Vec<FileToOpenOrCreate>,
213}
214
215/// Profiles structure
216#[derive(Debug, Clone, Serialize, Deserialize)]
217pub struct Profiles {
218	pub all:Vec<serde_json::Value>,
219	pub home:String,
220	pub profile:serde_json::Value,
221}
222
223/// Logger structure
224#[derive(Debug, Clone, Serialize, Deserialize)]
225pub struct Logger {
226	pub resource:serde_json::Value,
227}
228
229/// OS information structure
230#[derive(Debug, Clone, Serialize, Deserialize)]
231pub struct OsInfo {
232	pub release:String,
233}
234
235/// Wind service adapter that bridges Mountain services to Wind's interfaces
236pub struct WindServiceAdapter {
237	runtime:Arc<ApplicationRunTime>,
238}
239
240impl WindServiceAdapter {
241	/// Create a new Wind service adapter
242	pub fn new(runtime:Arc<ApplicationRunTime>) -> Self {
243		dev_log!("ipc", "[WindServiceAdapters] Creating Wind service adapter");
244		Self { runtime }
245	}
246
247	/// Convert Mountain's sandbox configuration to Wind's desktop configuration
248	pub async fn convert_to_wind_configuration(
249		&self,
250		mountain_config:serde_json::Value,
251	) -> Result<WindDesktopConfiguration, String> {
252		dev_log!("ipc", "[WindServiceAdapters] Converting Mountain config to Wind config");
253
254		// Parse the Mountain configuration
255		let config:MountainSandboxConfiguration = serde_json::from_value(mountain_config)
256			.map_err(|e| format!("Failed to parse Mountain configuration: {}", e))?;
257
258		// Convert to Wind's format
259		let wind_config = WindDesktopConfiguration {
260			window_id:config.window_id.parse().unwrap_or(1),
261			app_root:config.app_root,
262			user_data_path:config.user_data_dir,
263			temp_path:config.tmp_dir,
264			log_level:config.log_level.to_string(),
265			is_packaged:config.product_configuration.is_packaged,
266			tauri_version:config.versions.mountain,
267			platform:config.platform,
268			arch:config.arch,
269			workspace:None,
270			files_to_open_or_create:None,
271			files_to_diff:None,
272			files_to_wait:None,
273			fullscreen:Some(false),
274			zoom_level:Some(config.zoom_level),
275			is_custom_zoom_level:Some(false),
276			profiles:Profiles { all:vec![], home:config.home_dir, profile:serde_json::Value::Null },
277			policies_data:None,
278			loggers:vec![],
279			backup_path:Some(config.backup_path),
280			disable_layout_restore:Some(false),
281			os:OsInfo { release:std::env::consts::OS.to_string() },
282		};
283
284		Ok(wind_config)
285	}
286
287	/// Get Wind-compatible environment service
288	pub async fn get_environment_service(&self) -> Result<WindEnvironmentService, String> {
289		dev_log!("ipc", "[WindServiceAdapters] Getting Wind environment service");
290
291		Ok(WindEnvironmentService::new())
292	}
293
294	/// Get Wind-compatible file service
295	pub async fn get_file_service(&self) -> Result<WindFileService, String> {
296		dev_log!("ipc", "[WindServiceAdapters] Getting Wind file service");
297
298		let file_system_reader:Arc<dyn FileSystemReader> = self.runtime.Environment.Require();
299
300		let file_system_writer:Arc<dyn FileSystemWriter> = self.runtime.Environment.Require();
301
302		Ok(WindFileService::new(file_system_reader, file_system_writer))
303	}
304
305	/// Get Wind-compatible storage service
306	pub async fn get_storage_service(&self) -> Result<WindStorageService, String> {
307		dev_log!("ipc", "[WindServiceAdapters] Getting Wind storage service");
308
309		let storage:Arc<dyn StorageProvider> = self.runtime.Environment.Require();
310
311		Ok(WindStorageService::new(storage))
312	}
313
314	/// Get Wind-compatible configuration service
315	pub async fn get_configuration_service(&self) -> Result<WindConfigurationService, String> {
316		dev_log!("ipc", "[WindServiceAdapters] Getting Wind configuration service");
317
318		let config:Arc<dyn ConfigurationProvider> = self.runtime.Environment.Require();
319
320		Ok(WindConfigurationService::new(config))
321	}
322}
323
324/// Wind environment service adapter
325pub struct WindEnvironmentService {
326	// Environment variables are accessed via std::env
327}
328
329impl WindEnvironmentService {
330	pub fn new() -> Self { Self {} }
331
332	pub async fn get_app_root(&self) -> Result<String, String> { std::env::var("APP_ROOT").map_err(|e| e.to_string()) }
333
334	pub async fn get_user_data_path(&self) -> Result<String, String> {
335		std::env::var("USER_DATA_PATH").map_err(|e| e.to_string())
336	}
337}
338
339/// Wind file service adapter
340pub struct WindFileService {
341	reader:Arc<dyn FileSystemReader>,
342	writer:Arc<dyn FileSystemWriter>,
343}
344
345impl WindFileService {
346	pub fn new(reader:Arc<dyn FileSystemReader>, writer:Arc<dyn FileSystemWriter>) -> Self { Self { reader, writer } }
347
348	pub async fn read_file(&self, path:String) -> Result<Vec<u8>, String> {
349		self.reader.ReadFile(&PathBuf::from(path)).await.map_err(|e| e.to_string())
350	}
351
352	pub async fn write_file(&self, path:String, content:Vec<u8>) -> Result<(), String> {
353		self.writer
354			.WriteFile(&PathBuf::from(path), content, true, true)
355			.await
356			.map_err(|e:CommonError| e.to_string())
357	}
358
359	pub async fn stat_file(&self, path:String) -> Result<serde_json::Value, String> {
360		let stat_dto = self
361			.reader
362			.StatFile(&PathBuf::from(path))
363			.await
364			.map_err(|e:CommonError| e.to_string())?;
365		Ok(json!(stat_dto))
366	}
367}
368
369/// Wind storage service adapter
370pub struct WindStorageService {
371	provider:Arc<dyn StorageProvider>,
372}
373
374impl WindStorageService {
375	pub fn new(provider:Arc<dyn StorageProvider>) -> Self { Self { provider } }
376
377	pub async fn get(&self, key:String) -> Result<serde_json::Value, String> {
378		let value = self
379			.provider
380			.GetStorageValue(false, &key)
381			.await
382			.map_err(|e:CommonError| e.to_string())?
383			.ok_or_else(|| "Storage key not found".to_string())?;
384		Ok(value)
385	}
386
387	pub async fn set(&self, key:String, value:serde_json::Value) -> Result<(), String> {
388		self.provider
389			.UpdateStorageValue(false, key.to_string(), Some(value))
390			.await
391			.map_err(|e:CommonError| e.to_string())
392	}
393}
394
395/// Wind configuration service adapter
396pub struct WindConfigurationService {
397	provider:Arc<dyn ConfigurationProvider>,
398}
399
400impl WindConfigurationService {
401	pub fn new(provider:Arc<dyn ConfigurationProvider>) -> Self { Self { provider } }
402
403	pub async fn get_value(&self, key:String) -> Result<serde_json::Value, String> {
404		self.provider
405			.GetConfigurationValue(Some(key.to_string()), ConfigurationOverridesDTO::default())
406			.await
407			.map_err(|e| e.to_string())
408	}
409
410	pub async fn update_value(&self, key:String, value:serde_json::Value) -> Result<(), String> {
411		self.provider
412			.UpdateConfigurationValue(
413				key,
414				value,
415				ConfigurationTarget::User,
416				ConfigurationOverridesDTO::default(),
417				None,
418			)
419			.await
420			.map_err(|e| e.to_string())
421	}
422}
423
424/// Mountain sandbox configuration structure
425#[derive(Debug, Clone, Serialize, Deserialize)]
426struct MountainSandboxConfiguration {
427	pub window_id:String,
428	pub machine_id:String,
429	pub session_id:String,
430	pub log_level:i32,
431	pub user_env:std::collections::HashMap<String, String>,
432	pub app_root:String,
433	pub app_name:String,
434	pub app_uri_scheme:String,
435	pub app_language:String,
436	pub app_host:String,
437	pub platform:String,
438	pub arch:String,
439	pub versions:Versions,
440	pub exec_path:String,
441	pub home_dir:String,
442	pub tmp_dir:String,
443	pub user_data_dir:String,
444	pub backup_path:String,
445	pub resources_path:String,
446	pub vscode_cwd:String,
447	pub nls:NLSConfiguration,
448	pub product_configuration:ProductConfiguration,
449	pub zoom_level:f64,
450}
451
452#[derive(Debug, Clone, Serialize, Deserialize)]
453struct Versions {
454	pub mountain:String,
455	pub electron:String,
456	pub chrome:String,
457	pub node:String,
458}
459
460#[derive(Debug, Clone, Serialize, Deserialize)]
461struct NLSConfiguration {
462	pub messages:std::collections::HashMap<String, String>,
463	pub language:String,
464	pub available_languages:std::collections::HashMap<String, String>,
465}
466
467#[derive(Debug, Clone, Serialize, Deserialize)]
468struct ProductConfiguration {
469	pub name_short:String,
470	pub name_long:String,
471	pub application_name:String,
472	pub embedder_identifier:String,
473	pub is_packaged:bool,
474}