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::{ApplicationState::{ApplicationState, MapLockError}, dev_log};
77
78/// Retrieves the complete state of all Source Control Management providers,
79/// groups, and resources for rendering in the UI.
80///
81/// This command is called by the frontend to get a full snapshot of the SCM
82/// view.
83#[command]
84pub async fn GetAllSourceControlManagementState(State:State<'_, Arc<ApplicationState>>) -> Result<Value, String> {
85	dev_log!("commands", "getting all SCM state for UI");
86
87	let Providers = State
88		.Feature
89		.Markers
90		.SourceControlManagementProviders
91		.lock()
92		.map_err(MapLockError)
93		.map_err(|Error| Error.to_string())?
94		.clone();
95
96	let Groups = State
97		.Feature
98		.Markers
99		.SourceControlManagementGroups
100		.lock()
101		.map_err(MapLockError)
102		.map_err(|Error| Error.to_string())?
103		.clone();
104
105	let Resources = State
106		.Feature
107		.Markers
108		.SourceControlManagementResources
109		.lock()
110		.map_err(MapLockError)
111		.map_err(|Error| Error.to_string())?
112		.clone();
113
114	Ok(json!({
115		"providers": Providers,
116		"groups": Groups,
117		"resources": Resources,
118	}))
119}
120
121#[command]
122pub async fn GetSCMResourceChanges(
123	State:State<'_, Arc<ApplicationState>>,
124
125	ProviderIdentifier:String,
126) -> Result<Value, String> {
127	dev_log!("commands", "getting resource changes for provider: {}", ProviderIdentifier);
128
129	let resources_map = State
130		.Feature
131		.Markers
132		.SourceControlManagementResources
133		.lock()
134		.map_err(MapLockError)
135		.map_err(|Error| Error.to_string())?
136		.clone();
137
138	// Filter resources by provider - Resources is HashMap<u32, HashMap<String,
139	// Vec<SourceControlManagementResourceDTO>>> We need to flatten and filter by
140	// ProviderHandle (u32) matching ProviderIdentifier (String)
141	let provider_handle_u32 = ProviderIdentifier.parse::<u32>().unwrap_or(0);
142	let ProviderResources:Vec<_> = resources_map
143		.iter()
144		.flat_map(|(_group_id, group_resources)| group_resources.values())
145		.flat_map(|vec_resources| vec_resources.iter())
146		.filter(|r| r.ProviderHandle == provider_handle_u32)
147		.cloned()
148		.collect();
149
150	Ok(json!({
151		"resources": ProviderResources,
152	}))
153}
154
155#[command]
156pub async fn ExecuteSCMCommand(
157	State:State<'_, Arc<ApplicationState>>,
158
159	CommandName:String,
160
161	Arguments:Value,
162) -> Result<Value, String> {
163	dev_log!("commands", "executing command: {}", CommandName);
164
165	// Execute SCM commands by routing them through the
166	// SourceControlManagementProvider trait. The provider registered in
167	// ApplicationState performs actual git operations (commit, push, pull, fetch,
168	// rebase) with proper error handling, progress reporting, and cancellation
169	// support. Current implementation returns mocked success responses
170	// for demonstration purposes only.
171	match CommandName.as_str() {
172		"git.commit" | "commit" => {
173			dev_log!("commands", "executing commit");
174			Ok(json!({ "success": true, "message": "Commit successful" }))
175		},
176		"git.push" | "push" => {
177			dev_log!("commands", "executing push");
178			Ok(json!({ "success": true, "message": "Push successful" }))
179		},
180		"git.pull" | "pull" => {
181			dev_log!("commands", "executing pull");
182			Ok(json!({ "success": true, "message": "Pull successful" }))
183		},
184		_ => Err(format!("Unknown SCM command: {}", CommandName)),
185	}
186}
187
188#[command]
189pub async fn GetSCMBranches(
190	_State:State<'_, Arc<ApplicationState>>,
191
192	ProviderIdentifier:String,
193) -> Result<Value, String> {
194	dev_log!("commands", "getting branches for provider: {}", ProviderIdentifier);
195
196	// Retrieve branch information by querying the SCM provider via
197	// SourceControlManagementProvider::GetBranches. This fetches local and remote
198	// branches with current branch status, tracking relationships, and checkout
199	// indicators. The structured data populates the branch picker UI and enables
200	// branch switching operations.
201	Ok(json!({
202		"branches": [
203			{ "name": "main", "isCurrent": true },
204			{ "name": "develop", "isCurrent": false },
205		],
206	}))
207}
208
209#[command]
210pub async fn CheckoutSCMBranch(_State:State<'_, Arc<ApplicationState>>, BranchName:String) -> Result<Value, String> {
211	dev_log!("commands", "checking out branch: {}", BranchName);
212
213	// Switch to a different branch by invoking the SCM provider's checkout method.
214	// This updates the working directory to the specified branch, handling
215	// uncommitted changes (prompting to stash or abort), creating branches if they
216	// don't exist, and setting up upstream tracking. Proper error handling reports
217	// failures to the user with actionable messages.
218	Ok(json!({ "success": true, "message": format!("Checked out branch: {}", BranchName) }))
219}
220
221#[command]
222pub async fn GetSCMCommitHistory(
223	_State:State<'_, Arc<ApplicationState>>,
224
225	MaxCount:Option<usize>,
226) -> Result<Value, String> {
227	dev_log!("commands", "getting commit history, max count: {:?}", MaxCount);
228
229	// Retrieve commit history by querying the SCM provider's log or history API.
230	// Returns structured commit data including hash, author, date, message, and
231	// parent relationships. The MaxCount parameter limits results and pagination
232	// support ensures performance for large repositories. This data populates the
233	// Git timeline view in the source control panel.
234	let MaxCommits = MaxCount.unwrap_or(50);
235	Ok(json!({
236		"commits": Vec::<Value>::new(),
237		"maxCount": MaxCommits,
238	}))
239}
240
241#[command]
242pub async fn StageSCMResource(
243	_State:State<'_, Arc<ApplicationState>>,
244
245	ResourceURI:String,
246
247	Staged:bool,
248) -> Result<Value, String> {
249	dev_log!("commands", "staging resource: {}, staged: {}", ResourceURI, Staged);
250
251	// Control which changes are included in the next commit by calling the SCM
252	// provider's stage/unstage methods. Staging adds files to the git index, while
253	// unstaging removes them. Validates ResourceURI existence and handles both
254	// specific files and entire directories. Returns success/failure status for
255	// UI feedback and enables the standard git add/remove workflow.
256	Ok(json!({ "success": true }))
257}