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}