Skip to main content

Mountain/ApplicationState/Internal/ExtensionScanner/
ScanAndPopulateExtensions.rs

1//! # ScanAndPopulateExtensions Module (Internal)
2//!
3//! ## RESPONSIBILITIES
4//! Scans all registered extension paths for valid extensions and populates the
5//! state with discovered extensions.
6//!
7//! ## ARCHITECTURAL ROLE
8//! ScanAndPopulateExtensions is part of the **Internal::ExtensionScanner**
9//! module, handling extension discovery and population.
10//!
11//! ## KEY COMPONENTS
12//! - ScanAndPopulateExtensions: Main function for scanning extensions
13//! - ScanExtensionsWithRecovery: Robust scanning with recovery
14//!
15//! ## ERROR HANDLING
16//! - Returns Result with CommonError on failure
17//! - Handles partial failures gracefully
18//! - Comprehensive error logging
19//!
20//! ## LOGGING
21//! Operations are logged at appropriate levels (info, debug, warn, error).
22//!
23//! ## PERFORMANCE CONSIDERATIONS
24//! - Async operations for scanning
25//! - Handles multiple scan paths
26//! - Partial failure handling
27//!
28//! ## TODO
29//! - [ ] Add concurrent scanning
30//! - [ ] Implement extension caching
31//! - [ ] Add extension validation rules
32
33use std::{collections::HashMap, path::PathBuf};
34
35use CommonLibrary::Error::CommonError::CommonError;
36use serde_json::Value;
37use tauri::AppHandle;
38
39use crate::{
40	ApplicationState::DTO::ExtensionDescriptionStateDTO::ExtensionDescriptionStateDTO,
41	ExtensionManagement,
42	dev_log,
43};
44
45/// Scans all registered extension paths for valid extensions and populates the
46/// state.
47///
48/// # Arguments
49/// * `ApplicationHandle` - Tauri application handle for extension management
50/// * `State` - Reference to the application state
51///
52/// # Returns
53/// Result indicating success or CommonError on failure
54///
55/// # Behavior
56/// - Scans all registered extension paths
57/// - Populates state with discovered extensions
58/// - Returns comprehensive scan statistics
59/// - Handles partial failures gracefully
60pub async fn ScanAndPopulateExtensions(
61	ApplicationHandle:AppHandle,
62
63	_State:&crate::ApplicationState::State::ExtensionState::State::State,
64) -> Result<(), CommonError> {
65	dev_log!("extensions", "[ExtensionScanner] Starting extension scan...");
66
67	let mut all_found_extensions:HashMap<String, ExtensionDescriptionStateDTO> = HashMap::new();
68
69	// Note: This would need to be adapted to the new state structure
70	// For now, this is a placeholder showing the structure
71	let scan_paths:Vec<PathBuf> = _State.Registry.GetExtensionScanPaths();
72
73	dev_log!("extensions", "[ExtensionScanner] Scanning paths: {:?}", scan_paths);
74
75	let mut successful_scans = 0;
76
77	let mut failed_scans = 0;
78
79	for path in scan_paths {
80		let path_clone = path.clone();
81
82		match ExtensionManagement::Scanner::ScanDirectoryForExtensions(ApplicationHandle.clone(), path_clone).await {
83			Ok(found_in_path) => {
84				successful_scans += 1;
85
86				let path_count = found_in_path.len();
87
88				let mut inserted_from_path = 0;
89
90				let mut rejected_empty_identifier = 0;
91
92				for extension in found_in_path {
93					let identifier = extension
94						.Identifier
95						.get("value")
96						.and_then(Value::as_str)
97						.unwrap_or_default()
98						.to_string();
99
100					if !identifier.is_empty() {
101						all_found_extensions.insert(identifier, extension);
102
103						inserted_from_path += 1;
104					} else {
105						rejected_empty_identifier += 1;
106
107						dev_log!(
108							"extensions",
109							"warn: [ExtensionScanner] Rejected extension '{}' - empty identifier (publisher='{}', \
110							 Identifier={:?})",
111							extension.Name,
112							extension.Publisher,
113							extension.Identifier
114						);
115					}
116				}
117
118				dev_log!(
119					"extensions",
120					"[ExtensionScanner] Path '{}' yielded {} parsed, {} inserted, {} rejected",
121					path.display(),
122					path_count,
123					inserted_from_path,
124					rejected_empty_identifier
125				);
126			},
127
128			Err(error) => {
129				failed_scans += 1;
130
131				dev_log!(
132					"extensions",
133					"warn: [ExtensionScanner] Failed to scan extension path '{}': {}",
134					path.display(),
135					error
136				);
137			},
138		}
139	}
140
141	// Store discovered extensions into ApplicationState
142	let post_write_count = {
143		let mut Guard = _State
144			.ScannedExtensions
145			.ScannedExtensions
146			.lock()
147			.map_err(|Error| CommonError::StateLockPoisoned { Context:Error.to_string() })?;
148
149		Guard.clear();
150
151		for (Key, Dto) in &all_found_extensions {
152			Guard.insert(Key.clone(), Dto.clone());
153		}
154
155		Guard.len()
156	};
157
158	dev_log!(
159		"extensions",
160		"[ExtensionScanner] Extension scan complete. Found {} extensions ({} successful scans, {} failed scans). \
161		 ScannedExtensions map now has {} entries.",
162		all_found_extensions.len(),
163		successful_scans,
164		failed_scans,
165		post_write_count
166	);
167
168	if failed_scans > 0 {
169		dev_log!(
170			"extensions",
171			"warn: [ExtensionScanner] {} extension paths failed to scan",
172			failed_scans
173		);
174	}
175
176	Ok(())
177}
178
179/// Robust extension scanning with comprehensive error handling.
180///
181/// # Arguments
182/// * `ApplicationHandle` - Tauri application handle for extension management
183/// * `State` - Reference to the application state
184///
185/// # Returns
186/// Result indicating success or CommonError on failure
187///
188/// # Behavior
189/// - Clears potentially corrupted extension state first
190/// - Performs the scan
191/// - Retries once on failure
192/// - Comprehensive error logging
193pub async fn ScanExtensionsWithRecovery(
194	ApplicationHandle:AppHandle,
195
196	State:&crate::ApplicationState::State::ExtensionState::State::State,
197) -> Result<(), CommonError> {
198	dev_log!(
199		"extensions",
200		"[ExtensionScanner] Starting robust extension scan with recovery..."
201	);
202
203	// Clear potentially corrupted extension state first
204	// Note: Would clear
205	// State.Extension.ScannedExtensions.Extension.ScannedExtensions
206
207	// Perform the scan
208	match ScanAndPopulateExtensions(ApplicationHandle.clone(), State).await {
209		Ok(()) => {
210			dev_log!("extensions", "[ExtensionScanner] Robust extension scan completed successfully");
211
212			Ok(())
213		},
214
215		Err(error) => {
216			dev_log!(
217				"extensions",
218				"error: [ExtensionScanner] Robust extension scan failed: {}",
219				error
220			);
221
222			// Attempt recovery by clearing state and retrying once
223			dev_log!(
224				"extensions",
225				"warn: [ExtensionScanner] Attempting recovery from extension scan failure..."
226			);
227
228			// Clear state again
229			// Note: Would clear
230			// State.Extension.ScannedExtensions.Extension.ScannedExtensions
231
232			// Retry the scan with a cloned handle
233			ScanAndPopulateExtensions(ApplicationHandle.clone(), State).await
234		},
235	}
236}