Skip to main content

Mountain/Command/
SourceControlManagement.rs

1//! # SourceControlManagement (Command)
2//!
3//! RESPONSIBILITIES:
4//! - Defines Tauri command handlers for Source Control Management (SCM)
5//!   operations
6//! - Exposes SCM functionality to the Sky frontend via IPC
7//! - Aggregates SCM provider state, resources, and groups for UI rendering
8//! - Routes SCM commands to appropriate providers (commit, push, pull, branch
9//!   ops)
10//! - Manages branch listing, checkout, and commit history retrieval
11//! - Handles resource staging (git add equivalent)
12//!
13//! ARCHITECTURAL ROLE:
14//! - Command module that bridges Sky UI requests to
15//! `SourceControlManagementProvider` implementations in the Environment
16//!   layer
17//! - Uses Tauri's `#[command]` attribute for IPC exposure
18//! - Reads from `ApplicationState.SourceControlManagement*` fields to gather
19//!   state
20//! - TODO: Should forward commands to provider methods via DI (Require trait)
21//!
22//! COMMAND REFERENCE (Tauri IPC):
23//! - [`GetAllSourceControlManagementState`]: Returns complete snapshot of
24//!   providers, groups, and resources for SCM view
25//! - [`GetSCMResourceChanges`]: Get file changes for a specific provider
26//! - [`ExecuteSCMCommand`]: Execute SCM operation (commit, push, pull, etc.)
27//! - [`GetSCMBranches`]: List branches for provider
28//! - [`CheckoutSCMBranch`]: Switch to a different branch
29//! - [`GetSCMCommitHistory`]: Retrieve commit log with optional limit
30//! - [`StageSCMResource`]: Stage or unstage a file resource
31//!
32//! ERROR HANDLING:
33//! - Returns `Result<Value, String>` where error string is sent to frontend
34//! - Uses `MapLockError` to convert Mutex poisoning to error strings
35//! - Provider identifier parsing may fail (unwrap_or(0) fallback)
36//! - Unknown commands return error string
37//!
38//! PERFORMANCE:
39//! - State access uses RwLock reads; cloning entire state maps (may be heavy)
40//! - TODO: Consider pagination for large commit histories and resource lists
41//!
42//! VS CODE REFERENCE:
43//! - `vs/workbench/contrib/scm/common/scm.ts` - SCM model and state aggregation
44//! - `vs/workbench/contrib/scm/browser/scmView.ts` - SCM UI panel
45//! - `vs/workbench/services/scm/common/scmService.ts` - SCM service façade
46//!
47//! TODO:
48//! - Integrate with `SourceControlManagementProvider` trait methods
49//! - Implement actual SCM command execution (currently stubs with mock success)
50//! - Add proper error handling for failed git operations
51//! - Implement branch retrieval with remote tracking branches
52//! - Add commit history with proper commit objects (author, message, hash)
53//! - Implement resource staging with correct file paths and states
54//! - Add support for stash operations, merging, rebasing
55//! - Handle multiple SCM providers simultaneously (git, svn, etc.)
56//! - Add cancellation tokens for long-running operations
57//! - Implement diff viewing with proper unified diff format
58//! - Add SCM resource decoration (git status icons, gutter marks)
59//! - Support SCM workspace edit (apply changes from commit/rebase)
60//! - Add SCM input box interactions (commit message, branch name)
61//!
62//! MODULE CONTENTS:
63//! - Tauri command functions (all `#[command]` async):
64//!   - State retrieval: `GetAllSourceControlManagementState`,
65//!     `GetSCMResourceChanges`
66//!   - Operations: `ExecuteSCMCommand`, `StageSCMResource`
67//!   - Branch management: `GetSCMBranches`, `CheckoutSCMBranch`
68//!   - History: `GetSCMCommitHistory`
69//! - No data structures (uses DTOs from CommonLibrary)
70
71use std::sync::Arc;
72
73use serde_json::{Value, json};
74use tauri::{State, command};
75
76use crate::{
77	ApplicationState::State::ApplicationState::{ApplicationState, MapLockError},
78	dev_log,
79};
80
81/// Retrieves the complete state of all Source Control Management providers,
82/// groups, and resources for rendering in the UI.
83///
84/// This command is called by the frontend to get a full snapshot of the SCM
85/// view.
86#[command]
87pub async fn GetAllSourceControlManagementState(State:State<'_, Arc<ApplicationState>>) -> Result<Value, String> {
88	dev_log!("commands", "getting all SCM state for UI");
89
90	let Providers = State
91		.Feature
92		.Markers
93		.SourceControlManagementProviders
94		.lock()
95		.map_err(MapLockError)
96		.map_err(|Error| Error.to_string())?
97		.clone();
98
99	let Groups = State
100		.Feature
101		.Markers
102		.SourceControlManagementGroups
103		.lock()
104		.map_err(MapLockError)
105		.map_err(|Error| Error.to_string())?
106		.clone();
107
108	let Resources = State
109		.Feature
110		.Markers
111		.SourceControlManagementResources
112		.lock()
113		.map_err(MapLockError)
114		.map_err(|Error| Error.to_string())?
115		.clone();
116
117	Ok(json!({
118		"providers": Providers,
119		"groups": Groups,
120		"resources": Resources,
121	}))
122}
123
124#[command]
125pub async fn GetSCMResourceChanges(
126	State:State<'_, Arc<ApplicationState>>,
127
128	ProviderIdentifier:String,
129) -> Result<Value, String> {
130	dev_log!("commands", "getting resource changes for provider: {}", ProviderIdentifier);
131
132	let resources_map = State
133		.Feature
134		.Markers
135		.SourceControlManagementResources
136		.lock()
137		.map_err(MapLockError)
138		.map_err(|Error| Error.to_string())?
139		.clone();
140
141	// Filter resources by provider - Resources is HashMap<u32, HashMap<String,
142	// Vec<SourceControlManagementResourceDTO>>> We need to flatten and filter by
143	// ProviderHandle (u32) matching ProviderIdentifier (String)
144	let provider_handle_u32 = ProviderIdentifier.parse::<u32>().unwrap_or(0);
145	let ProviderResources:Vec<_> = resources_map
146		.iter()
147		.flat_map(|(_group_id, group_resources)| group_resources.values())
148		.flat_map(|vec_resources| vec_resources.iter())
149		.filter(|r| r.ProviderHandle == provider_handle_u32)
150		.cloned()
151		.collect();
152
153	Ok(json!({
154		"resources": ProviderResources,
155	}))
156}
157
158#[command]
159pub async fn ExecuteSCMCommand(
160	State:State<'_, Arc<ApplicationState>>,
161
162	CommandName:String,
163
164	Arguments:Value,
165) -> Result<Value, String> {
166	dev_log!("commands", "executing command: {}", CommandName);
167
168	// Execute SCM commands by routing them through the
169	// SourceControlManagementProvider trait. The provider registered in
170	// ApplicationState performs actual git operations (commit, push, pull, fetch,
171	// rebase) with proper error handling, progress reporting, and cancellation
172	// support. Current implementation returns mocked success responses
173	// for demonstration purposes only.
174	match CommandName.as_str() {
175		"git.commit" | "commit" => {
176			dev_log!("commands", "executing commit");
177			Ok(json!({ "success": true, "message": "Commit successful" }))
178		},
179		"git.push" | "push" => {
180			dev_log!("commands", "executing push");
181			Ok(json!({ "success": true, "message": "Push successful" }))
182		},
183		"git.pull" | "pull" => {
184			dev_log!("commands", "executing pull");
185			Ok(json!({ "success": true, "message": "Pull successful" }))
186		},
187		_ => Err(format!("Unknown SCM command: {}", CommandName)),
188	}
189}
190
191#[command]
192pub async fn GetSCMBranches(
193	_State:State<'_, Arc<ApplicationState>>,
194
195	ProviderIdentifier:String,
196) -> Result<Value, String> {
197	dev_log!("commands", "getting branches for provider: {}", ProviderIdentifier);
198
199	// Retrieve branch information by querying the SCM provider via
200	// SourceControlManagementProvider::GetBranches. This fetches local and remote
201	// branches with current branch status, tracking relationships, and checkout
202	// indicators. The structured data populates the branch picker UI and enables
203	// branch switching operations.
204	Ok(json!({
205		"branches": [
206			{ "name": "main", "isCurrent": true },
207			{ "name": "develop", "isCurrent": false },
208		],
209	}))
210}
211
212#[command]
213pub async fn CheckoutSCMBranch(_State:State<'_, Arc<ApplicationState>>, BranchName:String) -> Result<Value, String> {
214	dev_log!("commands", "checking out branch: {}", BranchName);
215
216	// Switch to a different branch by invoking the SCM provider's checkout method.
217	// This updates the working directory to the specified branch, handling
218	// uncommitted changes (prompting to stash or abort), creating branches if they
219	// don't exist, and setting up upstream tracking. Proper error handling reports
220	// failures to the user with actionable messages.
221	Ok(json!({ "success": true, "message": format!("Checked out branch: {}", BranchName) }))
222}
223
224#[command]
225pub async fn GetSCMCommitHistory(
226	_State:State<'_, Arc<ApplicationState>>,
227
228	MaxCount:Option<usize>,
229) -> Result<Value, String> {
230	dev_log!("commands", "getting commit history, max count: {:?}", MaxCount);
231
232	// Retrieve commit history by querying the SCM provider's log or history API.
233	// Returns structured commit data including hash, author, date, message, and
234	// parent relationships. The MaxCount parameter limits results and pagination
235	// support ensures performance for large repositories. This data populates the
236	// Git timeline view in the source control panel.
237	let MaxCommits = MaxCount.unwrap_or(50);
238	Ok(json!({
239		"commits": Vec::<Value>::new(),
240		"maxCount": MaxCommits,
241	}))
242}
243
244#[command]
245pub async fn StageSCMResource(
246	_State:State<'_, Arc<ApplicationState>>,
247
248	ResourceURI:String,
249
250	Staged:bool,
251) -> Result<Value, String> {
252	dev_log!("commands", "staging resource: {}, staged: {}", ResourceURI, Staged);
253
254	// Control which changes are included in the next commit by calling the SCM
255	// provider's stage/unstage methods. Staging adds files to the git index, while
256	// unstaging removes them. Validates ResourceURI existence and handles both
257	// specific files and entire directories. Returns success/failure status for
258	// UI feedback and enables the standard git add/remove workflow.
259	Ok(json!({ "success": true }))
260}