Skip to content

Commit 3b69c26

Browse files
refactor(Mountain): Overhaul effect dispatching and Vine client
- Replaced ActionEffect-based dispatch with closure-based execution in Track module, simplifying type signatures and error handling - Updated Vine client to use correct gRPC methods (send_mountain_notification) and hashed request IDs - Centralized lock error handling via map_lock_error helper and propagated CommonError consistently - Migrated Cocoon management to Tauri's path resolver API with improved error propagation - Removed outdated comments and stubs across ApplicationState, ConfigurationProvider, and DocumentStateDTO - Added trivial_bounds feature to simplify trait bounds in Library These changes enhance IPC reliability with Cocoon, streamline effect execution, and improve error handling consistency throughout Mountain.
1 parent ae7c728 commit 3b69c26

15 files changed

Lines changed: 164 additions & 257 deletions

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ parking_lot = { workspace = true }
5252
lazy_static = { workspace = true }
5353
portable-pty = { workspace = true }
5454
prost = { workspace = true }
55+
http = { workspace = true }
5556

5657
# regex = { workspace = true }
5758
# unbug = { workspace = true }

Source/ApplicationState/ApplicationState.rs

Lines changed: 16 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@ use std::{
1111
sync::{
1212
Arc,
1313
Mutex as StandardMutex,
14+
PoisonError,
1415
atomic::{AtomicBool, AtomicU32, AtomicU64, Ordering as AtomicOrdering},
1516
},
1617
};
1718

19+
use Common::Error::CommonError::CommonError;
1820
use log::{error, info, warn};
19-
use tauri::{Manager, Wry};
21+
use tauri::Wry;
2022

2123
use super::{
2224
DTO::{
@@ -38,10 +40,6 @@ use super::{
3840
use crate::Environment::CommandProvider::CommandHandler;
3941

4042
/// The central, shared, thread-safe state for the entire Mountain application.
41-
///
42-
/// This struct consolidates all dynamic state required by the backend, from
43-
/// workspace information and configuration to the state of active UI components
44-
/// like terminals and WebViews.
4543
#[derive(Clone)]
4644
pub struct ApplicationState {
4745
// --- WorkSpace State ---
@@ -78,16 +76,12 @@ pub struct ApplicationState {
7876
pub ActiveTreeViews:Arc<StandardMutex<HashMap<String, TreeViewStateDTO>>>,
7977

8078
// --- IPC & User Interface State ---
81-
pub PendingUserInterfaceRequests: Arc<
82-
StandardMutex<
83-
HashMap<
84-
String,
85-
tokio::sync::oneshot::Sender<Result<serde_json::Value, Common::Error::CommonError::CommonError>>,
86-
>,
87-
>,
88-
>,
79+
pub PendingUserInterfaceRequests:
80+
Arc<StandardMutex<HashMap<String, tokio::sync::oneshot::Sender<Result<serde_json::Value, CommonError>>>>>,
8981
}
9082

83+
fn map_lock_error<T>(e:PoisonError<T>) -> CommonError { CommonError::StateLockPoisoned { Context:e.to_string() } }
84+
9185
impl Default for ApplicationState {
9286
fn default() -> Self {
9387
info!("[ApplicationState] Initializing default application state...");
@@ -114,8 +108,6 @@ impl Default for ApplicationState {
114108

115109
let GlobalMementoFilePath = Internal::ResolveMementoStorageFilePath(&ApplicationDataDirectoryPath, true, "");
116110
let InitialGlobalMementoMap = Internal::LoadInitialMementoFromDisk(&GlobalMementoFilePath);
117-
// let InitialCommandRegistryMap =
118-
// crate::Handler::Command::RegisterNativeCommands(); // TODO: Re-integrate
119111

120112
info!("[ApplicationState] Default state initialization complete.");
121113
Self {
@@ -128,7 +120,7 @@ impl Default for ApplicationState {
128120
GlobalMementoPath:GlobalMementoFilePath,
129121
WorkSpaceMemento:Arc::new(StandardMutex::new(HashMap::new())),
130122
WorkSpaceMementoPath:Arc::new(StandardMutex::new(None)),
131-
CommandRegistry:Arc::new(StandardMutex::new(HashMap::new())), // TODO: Use InitialCommandRegistryMap
123+
CommandRegistry:Arc::new(StandardMutex::new(HashMap::new())),
132124
DiagnosticsMap:Arc::new(StandardMutex::new(HashMap::new())),
133125
OpenDocuments:Arc::new(StandardMutex::new(HashMap::new())),
134126
OutputChannels:Arc::new(StandardMutex::new(HashMap::new())),
@@ -149,17 +141,16 @@ impl Default for ApplicationState {
149141
}
150142

151143
impl ApplicationState {
152-
/// Generates a unique, filesystem-safe identifier string for the current
144+
/// Generates a unique, filesystem-safe identifier for the current
153145
/// workspace.
154-
pub fn GetWorkSpaceIdentifier(&self) -> Result<String, String> {
155-
let LockErrorMapper = |e| format!("[AppState] Lock error: {}", e);
156-
let ConfigurationPathGuard = self.WorkSpaceConfigurationPath.lock().map_err(LockErrorMapper)?;
146+
pub fn GetWorkSpaceIdentifier(&self) -> Result<String, CommonError> {
147+
let ConfigurationPathGuard = self.WorkSpaceConfigurationPath.lock().map_err(map_lock_error)?;
157148
if let Some(ConfigurationPath) = ConfigurationPathGuard.as_ref() {
158149
return Ok(ConfigurationPath.file_name().unwrap_or_default().to_string_lossy().into_owned());
159150
}
160151
drop(ConfigurationPathGuard);
161152

162-
let FoldersGuard = self.WorkSpaceFolders.lock().map_err(LockErrorMapper)?;
153+
let FoldersGuard = self.WorkSpaceFolders.lock().map_err(map_lock_error)?;
163154
if let Some(FirstFolder) = FoldersGuard.first() {
164155
let PathString = FirstFolder.URI.path();
165156
// Create a more stable hash for the identifier.
@@ -184,14 +175,13 @@ impl ApplicationState {
184175
/// Updates the path to the workspace memento file and reloads its content
185176
/// from disk.
186177
pub fn UpdateWorkSpaceMementoPathAndReload(&self, ApplicationDataDirectory:&Path) -> Result<(), String> {
187-
let LockErrorMapper = |e| format!("[AppState] Lock error: {}", e);
188-
let WorkSpaceIdentifier = self.GetWorkSpaceIdentifier()?;
189-
let mut PathGuard = self.WorkSpaceMementoPath.lock().map_err(LockErrorMapper)?;
178+
let WorkSpaceIdentifier = self.GetWorkSpaceIdentifier().map_err(|e| e.to_string())?;
179+
let mut PathGuard = self.WorkSpaceMementoPath.lock().map_err(|e| e.to_string())?;
190180

191181
if WorkSpaceIdentifier == "NO_WORKSPACE" {
192182
if PathGuard.is_some() {
193183
*PathGuard = None;
194-
self.WorkSpaceMemento.lock().map_err(LockErrorMapper)?.clear();
184+
self.WorkSpaceMemento.lock().map_err(|e| e.to_string())?.clear();
195185
}
196186
return Ok(());
197187
}
@@ -206,7 +196,7 @@ impl ApplicationState {
206196
}
207197
*PathGuard = Some(NewMementoPath.clone());
208198
let NewMementoContent = Internal::LoadInitialMementoFromDisk(&NewMementoPath);
209-
*self.WorkSpaceMemento.lock().map_err(LockErrorMapper)? = NewMementoContent;
199+
*self.WorkSpaceMemento.lock().map_err(|e| e.to_string())? = NewMementoContent;
210200
}
211201
Ok(())
212202
}

Source/ApplicationState/DTO/DocumentStateDTO.rs

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,7 @@ use serde_json::Value;
88
use url::Url;
99

1010
use super::RPCModelContentChangeDTO::RPCModelContentChangeDTO;
11-
use crate::ApplicationState::Internal::{
12-
AnalyzeTextLinesAndEOL,
13-
URLSerializationHelper, /* DetectFileEncodingFromBytes,
14-
* DetectLanguageIdentifierFromFilePath, */
15-
};
11+
use crate::ApplicationState::Internal::{AnalyzeTextLinesAndEOL, URLSerializationHelper};
1612

1713
/// Represents the complete in-memory state of a single text document.
1814
#[derive(Serialize, Deserialize, Clone, Debug)]
@@ -39,9 +35,8 @@ impl DocumentStateDTO {
3935
/// Creates a new `DocumentStateDTO` from its initial content.
4036
pub fn Create(URI:Url, LanguageIdentifier:Option<String>, Content:String) -> Self {
4137
let (Lines, EOL) = AnalyzeTextLinesAndEOL(&Content);
42-
// A real implementation would have more robust language/encoding detection.
4338
let LanguageID = LanguageIdentifier.unwrap_or_else(|| "plaintext".to_string());
44-
let Encoding = "utf8".to_string(); // Stub for encoding detection
39+
let Encoding = "utf8".to_string();
4540

4641
Self {
4742
URI,
@@ -61,18 +56,14 @@ impl DocumentStateDTO {
6156
pub fn ToDTO(&self) -> Value { serde_json::to_value(self).unwrap_or(Value::Null) }
6257

6358
/// Applies a set of changes to the document.
64-
///
65-
/// This is a complex operation that simulates how a text buffer would be
66-
/// updated. For this implementation, it is simplified.
6759
pub fn ApplyChanges(&mut self, NewVersion:i64, ChangesValue:&Value) -> Result<(), String> {
6860
if NewVersion <= self.Version {
6961
return Ok(()); // Ignore stale changes
7062
}
7163

72-
let RPCChanges:Vec<RPCModelContentChangeDTO> = match serde_json::from_value(ChangesValue.clone()) {
64+
let _RPCChanges:Vec<RPCModelContentChangeDTO> = match serde_json::from_value(ChangesValue.clone()) {
7365
Ok(changes) => changes,
7466
Err(_) => {
75-
// Fallback for a full-content change, which is a common scenario.
7667
if let Some(FullText) = ChangesValue.as_str() {
7768
let (NewLines, NewEOL) = AnalyzeTextLinesAndEOL(FullText);
7869
self.Lines = NewLines;
@@ -85,10 +76,6 @@ impl DocumentStateDTO {
8576
},
8677
};
8778

88-
// A full implementation would require a rope data structure or complex logic
89-
// to apply deltas efficiently. We will log a warning and accept the new
90-
// version number, but the content will be out of sync until the next full
91-
// update.
9279
log::warn!(
9380
"Applying changes to {} by version bump only (delta application is a stub).",
9481
self.URI

Source/Environment/CommandProvider.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ pub enum CommandHandler<R:Runtime + 'static> {
3535
Proxied { SidecarIdentifier:String, CommandIdentifier:String },
3636
}
3737

38-
// Manually implement Clone because fn pointers are not automatically Clone
3938
impl<R:Runtime> Clone for CommandHandler<R> {
4039
fn clone(&self) -> Self {
4140
match self {

Source/Environment/ConfigurationProvider.rs

Lines changed: 13 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,6 @@ use serde_json::{Map, Value};
2727
use tauri::Manager;
2828

2929
use super::MountainEnvironment::MountainEnvironment;
30-
// TODO: Re-integrate IPC client for notifications
31-
// use crate::Vine::Client;
3230

3331
#[async_trait]
3432
impl ConfigurationProvider for MountainEnvironment {
@@ -59,16 +57,21 @@ impl ConfigurationProvider for MountainEnvironment {
5957
) -> Result<(), CommonError> {
6058
info!("[ConfigurationProvider] Updating key '{}' in target {:?}", Key, Target);
6159

62-
let ConfigPath = match Target {
60+
let config_path_result:Result<Option<PathBuf>, CommonError> = match Target {
6361
ConfigurationTarget::User => {
64-
self.ApplicationHandle.path().app_config_dir().map(|p| p.join("settings.json"))
62+
self.ApplicationHandle
63+
.path()
64+
.app_config_dir()
65+
.map(|p| Some(p.join("settings.json")))
66+
.map_err(|e| CommonError::ConfigurationLoad { Description:e.to_string() })
6567
},
6668
ConfigurationTarget::WorkSpace => {
67-
self.ApplicationState
69+
Ok(self
70+
.ApplicationState
6871
.WorkSpaceConfigurationPath
6972
.lock()
7073
.map_err(super::Utility::MapApplicationStateLockErrorToCommonError)?
71-
.clone()
74+
.clone())
7275
},
7376
_ => {
7477
return Err(CommonError::NotImplemented {
@@ -77,11 +80,12 @@ impl ConfigurationProvider for MountainEnvironment {
7780
},
7881
};
7982

83+
let ConfigPath = config_path_result?;
84+
8085
if let Some(Path) = ConfigPath {
8186
let mut CurrentConfig = ReadAndParseConfigurationFile(self, &Some(Path.clone())).await;
8287

8388
if let Value::Object(Map) = &mut CurrentConfig {
84-
// A more robust implementation would handle nested keys like "a.b.c".
8589
if ValueToSet.is_null() {
8690
Map.remove(&Key);
8791
} else {
@@ -95,8 +99,6 @@ impl ConfigurationProvider for MountainEnvironment {
9599
let FileSystemWriter:Arc<dyn FileSystemWriter> = self.Require();
96100
FileSystemWriter.WriteFile(&Path, ContentBytes, true, true).await?;
97101

98-
// After writing, trigger a full reload to update the in-memory state and notify
99-
// sidecars.
100102
InitializeAndMergeConfigurations(self).await;
101103
Ok(())
102104
} else {
@@ -116,15 +118,11 @@ impl ConfigurationInspector for MountainEnvironment {
116118
_Key:String,
117119
_Overrides:ConfigurationOverridesDTO,
118120
) -> Result<Option<InspectResultDataDTO>, CommonError> {
119-
// This is a complex operation requiring reading from all config files
120-
// without merging them to build the final DTO. It is a stub for now.
121121
warn!("[ConfigurationProvider] InspectConfigurationValue is not fully implemented.");
122122
Ok(None)
123123
}
124124
}
125125

126-
// --- Internal Helper Functions ---
127-
128126
/// An internal helper to read and parse a single JSON configuration file.
129127
async fn ReadAndParseConfigurationFile(Environment:&MountainEnvironment, Path:&Option<PathBuf>) -> Value {
130128
if let Some(p) = Path {
@@ -142,24 +140,21 @@ async fn ReadAndParseConfigurationFile(Environment:&MountainEnvironment, Path:&O
142140

143141
/// Logic to load and merge all configuration files into the effective
144142
/// configuration stored in `ApplicationState`.
145-
///
146-
/// This should be called at startup and after any settings file changes.
147143
pub async fn InitializeAndMergeConfigurations(Environment:&MountainEnvironment) {
148144
info!("[ConfigurationProvider] Initializing and merging all configurations...");
149145

150146
let UserSettingsPath = Environment
151147
.ApplicationHandle
152148
.path()
153149
.app_config_dir()
154-
.map(|p| p.join("settings.json"));
150+
.map(|p| p.join("settings.json"))
151+
.ok();
155152

156153
let WorkSpaceSettingsPath = Environment.ApplicationState.WorkSpaceConfigurationPath.lock().unwrap().clone();
157154

158155
let UserConfig = ReadAndParseConfigurationFile(Environment, &UserSettingsPath).await;
159156
let WorkSpaceConfig = ReadAndParseConfigurationFile(Environment, &WorkSpaceSettingsPath).await;
160157

161-
// A real implementation would also load default and folder-level settings.
162-
// The merge order is critical: workspace settings override user settings.
163158
let mut Merged = UserConfig.as_object().cloned().unwrap_or_default();
164159
if let Some(WorkSpaceMap) = WorkSpaceConfig.as_object() {
165160
for (k, v) in WorkSpaceMap {
@@ -171,17 +166,7 @@ pub async fn InitializeAndMergeConfigurations(Environment:&MountainEnvironment)
171166
Value::Object(Merged),
172167
);
173168

174-
// Update the central application state.
175169
*Environment.ApplicationState.Configuration.lock().unwrap() = FinalConfig.clone();
176170

177-
// TODO: Notify Cocoon of the change using the IPCProvider.
178-
// let Payload = serde_json::json!({ "keys": [], "source": 0 }); // Simplified
179-
// let IPCProvider: Arc<dyn IPCProvider> = Environment.Require();
180-
// IPCProvider.SendNotificationToSidecar(
181-
// "cocoon-main".to_string(),
182-
// "$acceptConfigurationChanged".to_string(),
183-
// serde_json::json!([Payload, FinalConfig]),
184-
// ).await.unwrap_or_else(|e| warn!("[ConfigurationProvider] Failed to notify
185-
// Cocoon of config change: {}", e));
186171
info!("[ConfigurationProvider] Configuration state updated and merged.");
187172
}

Source/Environment/OutputProvider.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ impl OutputChannelManager for MountainEnvironment {
2626
.lock()
2727
.map_err(Utility::MapApplicationStateLockErrorToCommonError)?;
2828

29-
// Create the channel if it doesn't exist.
3029
ChannelsGuard
3130
.entry(ChannelIdentifier.clone())
3231
.or_insert_with(|| OutputChannelStateDTO::Create(&Name, LanguageIdentifier.clone()));
@@ -123,9 +122,7 @@ impl OutputChannelManager for MountainEnvironment {
123122
}
124123

125124
/// Closes the view of an output channel in the UI.
126-
async fn Close(&self, ChannelIdentifier:String) -> Result<(), CommonError> {
127-
// This would typically just update the IsVisible flag and emit an event.
128-
// For simplicity, this is a stub.
125+
async fn Close(&self, _ChannelIdentifier:String) -> Result<(), CommonError> {
129126
warn!("[OutputProvider] Close is not fully implemented.");
130127
Ok(())
131128
}

Source/Environment/SecretProvider.rs

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,12 @@ use Common::{Error::CommonError::CommonError, Secret::SecretProvider::SecretProv
88
use async_trait::async_trait;
99
use keyring::Entry;
1010
use log::{info, trace};
11-
use tauri::Manager;
1211

1312
use super::MountainEnvironment::MountainEnvironment;
1413

1514
/// Constructs the service name for the keyring entry.
16-
///
17-
/// This is a crucial security feature that namespaces secrets on a
18-
/// per-extension basis, preventing one extension from reading another's
19-
/// secrets. It combines the application's unique identifier with the
20-
/// extension's identifier.
2115
fn GetKeyringServiceName(Environment:&MountainEnvironment, ExtensionIdentifier:&str) -> String {
22-
format!("{}.{}", Environment.ApplicationHandle.config().identifier, ExtensionIdentifier)
16+
format!("{}.{}", Environment.ApplicationHandle.package_info().name, ExtensionIdentifier)
2317
}
2418

2519
#[async_trait]
@@ -36,7 +30,7 @@ impl SecretProvider for MountainEnvironment {
3630

3731
match Entry.get_password() {
3832
Ok(Password) => Ok(Some(Password)),
39-
Err(keyring::Error::NoEntry) => Ok(None), // Not finding a secret is not an error.
33+
Err(keyring::Error::NoEntry) => Ok(None),
4034
Err(e) => Err(CommonError::SecretsAccess { Key, Reason:e.to_string() }),
4135
}
4236
}
@@ -66,8 +60,6 @@ impl SecretProvider for MountainEnvironment {
6660
let Entry = Entry::new(&ServiceName, &Key)
6761
.map_err(|e| CommonError::SecretsAccess { Key:Key.clone(), Reason:e.to_string() })?;
6862

69-
// This operation is idempotent; it is considered successful even if the
70-
// entry doesn't exist.
7163
match Entry.delete_credential() {
7264
Ok(_) | Err(keyring::Error::NoEntry) => Ok(()),
7365
Err(e) => Err(CommonError::SecretsAccess { Key, Reason:e.to_string() }),

Source/Library.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
//! components.
77
88
#![allow(non_snake_case, non_camel_case_types)]
9+
#![feature(trivial_bounds)]
910

1011
pub mod ApplicationState;
1112
pub mod Environment;

0 commit comments

Comments
 (0)