Mountain/Environment/
StatusBarProvider.rs

1// File: Mountain/Source/Environment/StatusBarProvider.rs
2// Role: Implements the `StatusBarProvider` trait for the `MountainEnvironment`.
3// Responsibilities:
4//   - Handle creating, updating, and removing status bar items and messages.
5//   - Orchestrate communication between the `Cocoon` sidecar and the `Sky`
6//     frontend.
7//   - Store status bar state in `ApplicationState` and push updates to the UI.
8
9//! # StatusBarProvider Implementation
10//!
11//! Implements the `StatusBarProvider` trait for the `MountainEnvironment`. This
12//! provider handles creating, updating, and removing status bar items, and
13//! orchestrates communication between the `Cocoon` sidecar and the `Sky`
14//! frontend.
15
16#![allow(non_snake_case, non_camel_case_types)]
17
18use std::sync::Arc;
19
20use Common::{
21	Environment::Requires::Requires,
22	Error::CommonError::CommonError,
23	IPC::{DTO::ProxyTarget::ProxyTarget, IPCProvider::IPCProvider},
24	StatusBar::{DTO::StatusBarEntryDTO::StatusBarEntryDTO, StatusBarProvider::StatusBarProvider},
25};
26use async_trait::async_trait;
27use log::info;
28use serde_json::{Value, json};
29use tauri::Emitter;
30
31use super::{MountainEnvironment::MountainEnvironment, Utility};
32
33#[async_trait]
34impl StatusBarProvider for MountainEnvironment {
35	/// Creates a new status bar entry or updates an existing one.
36	async fn SetStatusBarEntry(&self, Entry:StatusBarEntryDTO) -> Result<(), CommonError> {
37		info!("[StatusBarProvider] Setting entry: {}", Entry.EntryIdentifier);
38
39		let mut ItemsGuard = self
40			.ApplicationState
41			.ActiveStatusBarItems
42			.lock()
43			.map_err(Utility::MapApplicationStateLockErrorToCommonError)?;
44
45		ItemsGuard.insert(Entry.EntryIdentifier.clone(), Entry.clone());
46
47		drop(ItemsGuard);
48
49		self.ApplicationHandle
50			.emit("sky://statusbar/set-entry", Entry)
51			.map_err(|Error| CommonError::UserInterfaceInteraction { Reason:Error.to_string() })
52	}
53
54	/// Removes a status bar item from the UI.
55	async fn DisposeStatusBarEntry(&self, EntryIdentifier:String) -> Result<(), CommonError> {
56		info!("[StatusBarProvider] Disposing entry: {}", EntryIdentifier);
57
58		self.ApplicationState
59			.ActiveStatusBarItems
60			.lock()
61			.map_err(Utility::MapApplicationStateLockErrorToCommonError)?
62			.remove(&EntryIdentifier);
63
64		self.ApplicationHandle
65			.emit("sky://statusbar/dispose-entry", json!({ "EntryIdentifier": EntryIdentifier }))
66			.map_err(|Error| CommonError::UserInterfaceInteraction { Reason:Error.to_string() })
67	}
68
69	/// Shows a temporary message in the status bar.
70	async fn SetStatusBarMessage(&self, MessageIdentifier:String, Text:String) -> Result<(), CommonError> {
71		info!("[StatusBarProvider] Setting status message '{}': {}", MessageIdentifier, Text);
72
73		self.ApplicationHandle
74			.emit("sky://statusbar/set-message", json!({ "id": MessageIdentifier, "text": Text }))
75			.map_err(|Error| CommonError::UserInterfaceInteraction { Reason:Error.to_string() })
76	}
77
78	/// Disposes of a temporary status bar message.
79	async fn DisposeStatusBarMessage(&self, MessageIdentifier:String) -> Result<(), CommonError> {
80		info!("[StatusBarProvider] Disposing status message '{}'", MessageIdentifier);
81
82		self.ApplicationHandle
83			.emit("sky://statusbar/dispose-message", json!({ "id": MessageIdentifier }))
84			.map_err(|Error| CommonError::UserInterfaceInteraction { Reason:Error.to_string() })
85	}
86
87	/// Resolves a dynamic tooltip by making a reverse call to the extension
88	/// host.
89	async fn ProvideTooltip(&self, EntryIdentifier:String) -> Result<Option<Value>, CommonError> {
90		info!("[StatusBarProvider] Providing dynamic tooltip for entry: {}", EntryIdentifier);
91
92		let IPCProvider:Arc<dyn IPCProvider> = self.Require();
93
94		// This is a "reverse" call, where the host needs data from the sidecar.
95		let RPCMethod = format!("{}$ProvideStatusbarTooltip", ProxyTarget::ExtHostStatusBar.GetTargetPrefix());
96
97		let RPCResponse = IPCProvider
98			.SendRequestToSideCar("cocoon-main".to_string(), RPCMethod, json!([EntryIdentifier]), 5000)
99			.await?;
100
101		// If the response is null or fails to parse, we gracefully return None.
102		Ok(serde_json::from_value(RPCResponse).unwrap_or(None))
103	}
104}