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