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}