Mountain/Environment/
SourceControlManagementProvider.rs

1//! # SourceControlManagementProvider (Environment)
2//!
3//! Implements the `SourceControlManagementProvider` trait for
4//! `MountainEnvironment`, providing Git and other source control management
5//! (SCM) capabilities to the application.
6//!
7//! ## RESPONSIBILITIES
8//!
9//! ### 1. Repository Detection
10//! - Scan workspace folders for Git repositories
11//! - Detect repository root directories
12//! - Track repository state (clean, dirty, branching)
13//! - Monitor repository changes (commit, checkout, merge)
14//!
15//! ### 2. SCM Providers
16//! - Create and manage `SourceControlManagementProvider` instances
17//! - Support multiple SCM systems (Git, Mercurial, etc.)
18//! - Load extension-provided SCM providers
19//! - Route SCM operations to appropriate provider
20//!
21//! ### 3. Change Management
22//! - Track file changes (modified, added, deleted, renamed)
23//! - Provide diff information for changed files
24//! - Support staging and unstaging changes
25//! - Handle merge conflicts
26//!
27//! ### 4. Authentication
28//! - Manage SCM credentials and authentication
29//! - Support SSH keys and HTTPS tokens
30//! - Store credentials securely via `SecretProvider`
31//! - Handle authentication failures and prompts
32//!
33//! ### 5. Operations
34//! - Commit, push, pull, fetch operations
35//! - Branch management (create, delete, rename, checkout)
36//! - Merge and rebase operations
37//! - Remote management (add, remove, rename)
38//!
39//! ## ARCHITECTURAL ROLE
40//!
41//! SourceControlManagementProvider is the **SCM integration layer**:
42//!
43//! ```text
44//! UI (SCM View) ──► SourceControlManagementProvider ──► Git CLI / Libgit2
45//!                              │
46//!                              └─► Extension SCM Providers
47//! ```
48//!
49//! ### Position in Mountain
50//! - `Environment` module: SCM capability provider
51//! - Implements
52//!   `CommonLibrary::SourceControlManagement::SourceControlManagementProvider`
53//!   trait
54//! - Accessible via `Environment.Require<dyn
55//!   SourceControlManagementProvider>()`
56//!
57//! ### SCM Provider Hierarchy
58//! - **Built-in Git Provider**: Native Git implementation (preferred)
59//! - **Extension Providers**: Custom SCM support (Mercurial, SVN, etc.)
60//! - **Fallback Providers**: Basic functionality for unknown SCM types
61//!
62//! ### Dependencies
63//! - `SecretProvider`: For storing SCM credentials
64//! - `FileSystemReader` / `FileSystemWriter`: For .git operations
65//! - `Log`: SCM operation logging
66//! - External Git binary or libgit2 library
67//!
68//! ### Dependents
69//! - SCM UI view: Display repository state and changes
70//! - Source control commands: Commit, push, pull, etc.
71//! - `Binary::Main`::`MountainGetWorkbenchConfiguration`: SCM state
72//! - Extension SCM providers: Custom SCM implementations
73//!
74//! ## DATA MODEL
75//!
76//! Stored in `ApplicationState`:
77//! - `SourceControlManagementProviders`: Registered providers by ID
78//! - `SourceControlManagementGroups`: Repository groups (by workspace)
79//! - `SourceControlManagementResources`: Resource state (changed files)
80//!
81//! Key structures:
82//! - `SourceControlManagementProviderDTO`: Provider metadata
83//! - `SourceControlManagementGroupDTO`: Repository group state
84//! - `SourceControlManagementResourceDTO`: Changed file information
85//!
86//! ## REPOSITORY STATES
87//!
88//! - **Clean**: No uncommitted changes
89//! - **Dirty**: Unstaged changes present
90//! - **Staged**: Changes staged for commit
91//! - **Merging**: Merge in progress
92//! - **Rebasing**: Rebase in progress
93//! - **Cherry-picking**: Cherry-pick in progress
94//!
95//! ## ERROR HANDLING
96//!
97//! - Repository not found: `CommonError::SCMNotFound`
98//! - Authentication failure: `CommonError::SCMAuthenticationFailed`
99//! - Operation failure: `CommonError::SCMOperationFailed`
100//! - Merge conflict: `CommonError::SCMConflict`
101//! - Uncommitted changes: `CommonError::SCMUncommittedChanges`
102//!
103//! ## PERFORMANCE
104//!
105//! - Repository scanning should be async and cached
106//! - Use file system watchers to detect changes
107//! - Batch operations when possible (e.g., status of multiple files)
108//! - Consider background indexing for large repositories
109//!
110//! ## VS CODE REFERENCE
111//!
112//! Patterns from VS Code:
113//! - `vs/workbench/services/scm/common/scmService.ts` - SCM service
114//! - `vs/platform/scm/common/scm.ts` - SCM provider interface
115//! - `vs/sourcecontrol/git/common/git.ts` - Git provider implementation
116//!
117//! ## TODO
118//!
119//! - [ ] Implement built-in Git provider using libgit2 or Git CLI
120//! - [ ] Add repository discovery and change detection
121//! - [ ] Support staging, unstaging, and committing changes
122//! - [ ] Implement branch management UI and operations
123//! - [ ] Add remote operations (push, pull, fetch)
124//! - [ ] Handle merge conflicts with UI resolution
125//! - [ ] Support Git LFS and submodules
126//! - [ ] Add SCM authentication and credential management
127//! - [ ] Implement SCM extensions API for custom providers
128//! - [ ] Add SCM history and blame views
129//! - [ ] Support stash and pop operations
130//! - [ ] Implement tag management
131//! - [ ] Add SCM configuration and settings
132//! - [ ] Support detached HEAD and bisect operations
133//! - [ ] Implement SCM telemetry and diagnostics
134//!
135//! ## MODULE CONTENTS
136//!
137//! - [`SourceControlManagementProvider`]: Main struct implementing the trait
138//! - Repository detection and tracking
139//! - Provider registration and routing
140//! - SCM operation implementations
141//! - Authentication and credential management
142
143// `MountainEnvironment`. Responsibilities:
144//   - Manage source control providers (e.g., Git, Mercurial, SVN).
145//   - Handle SCM provider registration and disposal.
146//   - Manage resource groups (e.g., changes, untracked, merge conflicts).
147//   - Handle input boxes for user input (e.g., commit messages).
148//   - Emit events to the Sky frontend for UI updates.
149//   - Provide Git integration patterns for common operations.
150//   - Handle conflict detection and resolution.
151//   - Support multiple SCM providers simultaneously.
152//
153// TODOs:
154//   - Implement complete Git integration (status, commit, push, pull, branch)
155//   - Add Git diff display with visual comparison
156//   - Implement merge conflict resolution UI
157//   - Support Git staging/unstaging of resources
158//   - Add Git stash operations
159//   - Implement Git branch management (create, delete, checkout)
160//   - Support Git remote operations
161//   - Add Git history/log viewing
162//   - Implement Git blame annotations
163//   - Support Git submodules
164//   - Implement Git LFS (Large File Storage) support
165//   - Add Git tag management
166//   - Custom implementation for Mercurial, SVN, and other VCS
167//   - Implement SCM provider command registration
168//   - Support SCM provider decoration (badges, colors)
169//   - Add input box validation and validation messaging
170//   - Implement resource state caching for performance
171//   - Support SCM provider quick picks and menus
172//   - Add keyboard shortcuts for common SCM operations
173//   - Implement SCM provider extension points
174//   - Support Git rebase and cherry-pick operations
175//   - Add Git bisect support
176//   - Implement Git commit graph visualization
177//   - Support Git hooks integration
178//   - Add Git signature verification
179//   - Implement Git ignore management
180//
181// Inspired by VSCode's SCM service which:
182// - Provides a flexible abstraction over multiple source control systems
183// - Manages resource state changes through groups
184// - Supports provider-specific operations through commands
185// - Handles UI updates through event emission
186// - Manages input boxes for user interaction
187// - Git integration is the primary implementation with patterns for others
188//! # SourceControlManagementProvider Implementation
189//!
190//! Implements the `SourceControlManagementProvider` trait for the
191//! `MountainEnvironment`.
192//!
193//! ## SCM Provider Architecture
194//!
195//! Each SCM provider maintains:
196//! - **Handle**: Unique identifier for the provider
197//! - **Label**: User-friendly name (e.g., "Git")
198//! - **Root URI**: URI of the repository root
199//! - **Groups**: Resource groups organizing changed resources
200//! - **Input Box**: User input widget for operations (e.g., commit messages)
201//! - **Count**: Badge count for changed items
202//!
203//! ## Resource Groups
204//!
205//! Groups organize resources by their state:
206//! - **Changes**: Modified files ready to commit
207//! - **Untracked**: New files not yet tracked
208//! - **Staged**: Files staged for commit
209//! - **Merge Changes**: Files with merge conflicts
210//! - **Conflict Unresolved**: Unresolved conflict markers
211//
212//! ## SCM Lifecycle
213//!
214//! 1. **Create Provider**: Register a new SCM provider with handle and metadata
215//! 2. **Update Provider**: Update provider state (badge count, input box)
216//! 3. **Update Group**: Add or remove resources from groups
217//! 4. **Register Input Box**: Create input widget for user interaction
218//! 5. **Dispose Provider**: Remove provider and all associated state
219//
220//! ## Git Integration Patterns
221//!
222//! Typical Git provider workflow:
223//! - Detect Git repository via `.git` directory
224//! - Run `git status` to populate resource groups
225//! - Run `git diff` to provide file diffs
226//! - Use input box for commit messages
227//! - Show badge count for changed files
228//! - Provide commands: Stage, Unstage, Commit, Push, Pull, Discard
229
230use CommonLibrary::{
231	Error::CommonError::CommonError,
232	SourceControlManagement::{
233		DTO::{
234			SourceControlCreateDTO::SourceControlCreateDTO,
235			SourceControlGroupUpdateDTO::SourceControlGroupUpdateDTO,
236			SourceControlInputBoxDTO::SourceControlInputBoxDTO,
237			SourceControlManagementGroupDTO::SourceControlManagementGroupDTO,
238			SourceControlManagementProviderDTO::SourceControlManagementProviderDTO,
239			SourceControlUpdateDTO::SourceControlUpdateDTO,
240		},
241		SourceControlManagementProvider::SourceControlManagementProvider,
242	},
243};
244use async_trait::async_trait;
245use log::{info, warn};
246use serde_json::{Value, json};
247use tauri::Emitter;
248
249use super::{MountainEnvironment::MountainEnvironment, Utility};
250
251#[async_trait]
252impl SourceControlManagementProvider for MountainEnvironment {
253	async fn CreateSourceControl(&self, ProviderDataValue:Value) -> Result<u32, CommonError> {
254		let ProviderData:SourceControlCreateDTO = serde_json::from_value(ProviderDataValue)?;
255
256		let Handle = self.ApplicationState.GetNextSourceControlManagementProviderHandle();
257
258		info!(
259			"[SourceControlManagementProvider] Creating new SCM provider with handle {}",
260			Handle
261		);
262
263		let ProviderState = SourceControlManagementProviderDTO {
264			Handle,
265			Label:ProviderData.Label,
266			RootURI:Some(json!({ "external": ProviderData.RootUri.to_string() })),
267			CommitTemplate:None,
268			Count:None,
269			InputBox:None,
270		};
271
272		self.ApplicationState
273			.Feature
274			.Markers
275			.SourceControlManagementProviders
276			.lock()
277			.map_err(Utility::MapApplicationStateLockErrorToCommonError)?
278			.insert(Handle, ProviderState.clone());
279
280		self.ApplicationState
281			.Feature
282			.Markers
283			.SourceControlManagementGroups
284			.lock()
285			.map_err(Utility::MapApplicationStateLockErrorToCommonError)?
286			.insert(Handle, Default::default());
287
288		self.ApplicationHandle
289			.emit("sky://scm/provider/added", ProviderState)
290			.map_err(|Error| {
291				CommonError::UserInterfaceInteraction { Reason:format!("Failed to emit scm event: {}", Error) }
292			})?;
293
294		Ok(Handle)
295	}
296
297	async fn DisposeSourceControl(&self, ProviderHandle:u32) -> Result<(), CommonError> {
298		info!(
299			"[SourceControlManagementProvider] Disposing SCM provider with handle {}",
300			ProviderHandle
301		);
302
303		self.ApplicationState
304			.Feature
305			.Markers
306			.SourceControlManagementProviders
307			.lock()
308			.map_err(Utility::MapApplicationStateLockErrorToCommonError)?
309			.remove(&ProviderHandle);
310
311		self.ApplicationState
312			.Feature
313			.Markers
314			.SourceControlManagementGroups
315			.lock()
316			.map_err(Utility::MapApplicationStateLockErrorToCommonError)?
317			.remove(&ProviderHandle);
318
319		self.ApplicationHandle
320			.emit("sky://scm/provider/removed", ProviderHandle)
321			.map_err(|Error| CommonError::UserInterfaceInteraction { Reason:Error.to_string() })?;
322
323		Ok(())
324	}
325
326	async fn UpdateSourceControl(&self, ProviderHandle:u32, UpdateDataValue:Value) -> Result<(), CommonError> {
327		let UpdateData:SourceControlUpdateDTO = serde_json::from_value(UpdateDataValue)?;
328
329		info!("[SourceControlManagementProvider] Updating provider {}", ProviderHandle);
330
331		let mut ProvidersGuard = self
332			.ApplicationState
333			.Feature
334			.Markers
335			.SourceControlManagementProviders
336			.lock()
337			.map_err(Utility::MapApplicationStateLockErrorToCommonError)?;
338
339		if let Some(Provider) = ProvidersGuard.get_mut(&ProviderHandle) {
340			if let Some(count) = UpdateData.Count {
341				Provider.Count = Some(count);
342			}
343
344			if let Some(value) = UpdateData.InputBoxValue {
345				if let Some(input_box) = &mut Provider.InputBox {
346					input_box.Value = value;
347				}
348			}
349
350			let ProviderClone = Provider.clone();
351
352			// Release lock before emitting
353			drop(ProvidersGuard);
354
355			self.ApplicationHandle
356				.emit(
357					"sky://scm/provider/changed",
358					json!({ "handle": ProviderHandle, "provider": ProviderClone }),
359				)
360				.map_err(|Error| CommonError::UserInterfaceInteraction { Reason:Error.to_string() })?;
361		}
362
363		Ok(())
364	}
365
366	async fn UpdateSourceControlGroup(&self, ProviderHandle:u32, GroupDataValue:Value) -> Result<(), CommonError> {
367		let GroupData:SourceControlGroupUpdateDTO = serde_json::from_value(GroupDataValue)?;
368
369		info!(
370			"[SourceControlManagementProvider] Updating group '{}' for provider {}",
371			GroupData.GroupID, ProviderHandle
372		);
373
374		let mut GroupsGuard = self
375			.ApplicationState
376			.Feature
377			.Markers
378			.SourceControlManagementGroups
379			.lock()
380			.map_err(Utility::MapApplicationStateLockErrorToCommonError)?;
381
382		if let Some(ProviderGroups) = GroupsGuard.get_mut(&ProviderHandle) {
383			let Group = ProviderGroups.entry(GroupData.GroupID.clone()).or_insert_with(|| {
384				SourceControlManagementGroupDTO {
385					ProviderHandle,
386					Identifier:GroupData.GroupID.clone(),
387					Label:GroupData.Label.clone(),
388				}
389			});
390
391			Group.Label = GroupData.Label;
392
393			let GroupClone = Group.clone();
394
395			// Release lock before emitting
396			drop(GroupsGuard);
397
398			self.ApplicationHandle
399				.emit(
400					"sky://scm/group/changed",
401					json!({ "providerHandle": ProviderHandle, "group": GroupClone }),
402				)
403				.map_err(|Error| CommonError::UserInterfaceInteraction { Reason:Error.to_string() })?;
404		} else {
405			warn!(
406				"[SourceControlManagementProvider] Received group update for unknown provider handle: {}",
407				ProviderHandle
408			);
409		}
410
411		Ok(())
412	}
413
414	async fn RegisterInputBox(&self, ProviderHandle:u32, InputBoxDataValue:Value) -> Result<(), CommonError> {
415		let InputBoxData:SourceControlInputBoxDTO = serde_json::from_value(InputBoxDataValue)?;
416
417		info!(
418			"[SourceControlManagementProvider] Registering input box for provider {}",
419			ProviderHandle
420		);
421
422		let mut ProvidersGuard = self
423			.ApplicationState
424			.Feature
425			.Markers
426			.SourceControlManagementProviders
427			.lock()
428			.map_err(Utility::MapApplicationStateLockErrorToCommonError)?;
429
430		if let Some(Provider) = ProvidersGuard.get_mut(&ProviderHandle) {
431			Provider.InputBox = Some(InputBoxData);
432
433			let ProviderClone = Provider.clone();
434
435			// Release lock before emitting
436			drop(ProvidersGuard);
437
438			self.ApplicationHandle
439				.emit(
440					"sky://scm/provider/changed",
441					json!({ "handle": ProviderHandle, "provider": ProviderClone }),
442				)
443				.map_err(|Error| CommonError::UserInterfaceInteraction { Reason:Error.to_string() })?;
444		}
445
446		Ok(())
447	}
448}