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	_State:&crate::ApplicationState::ExtensionState::State::State,
63) -> Result<(), CommonError> {
64	dev_log!("extensions", "[ExtensionScanner] Starting extension scan...");
65
66	let mut all_found_extensions:HashMap<String, ExtensionDescriptionStateDTO> = HashMap::new();
67
68	// Note: This would need to be adapted to the new state structure
69	// For now, this is a placeholder showing the structure
70	let scan_paths:Vec<PathBuf> = _State.Registry.GetExtensionScanPaths();
71
72	dev_log!("extensions", "[ExtensionScanner] Scanning paths: {:?}", scan_paths);
73
74	let mut successful_scans = 0;
75	let mut failed_scans = 0;
76
77	for path in scan_paths {
78		let path_clone = path.clone();
79		match ExtensionManagement::Scanner::ScanDirectoryForExtensions(ApplicationHandle.clone(), path_clone).await {
80			Ok(found_in_path) => {
81				successful_scans += 1;
82				let path_count = found_in_path.len();
83				let mut inserted_from_path = 0;
84				let mut rejected_empty_identifier = 0;
85				for extension in found_in_path {
86					let identifier = extension
87						.Identifier
88						.get("value")
89						.and_then(Value::as_str)
90						.unwrap_or_default()
91						.to_string();
92
93					if !identifier.is_empty() {
94						all_found_extensions.insert(identifier, extension);
95						inserted_from_path += 1;
96					} else {
97						rejected_empty_identifier += 1;
98						dev_log!(
99							"extensions",
100							"warn: [ExtensionScanner] Rejected extension '{}' — empty identifier (publisher='{}', \
101							 Identifier={:?})",
102							extension.Name,
103							extension.Publisher,
104							extension.Identifier
105						);
106					}
107				}
108				dev_log!(
109					"extensions",
110					"[ExtensionScanner] Path '{}' yielded {} parsed, {} inserted, {} rejected",
111					path.display(),
112					path_count,
113					inserted_from_path,
114					rejected_empty_identifier
115				);
116			},
117			Err(error) => {
118				failed_scans += 1;
119				dev_log!(
120					"extensions",
121					"warn: [ExtensionScanner] Failed to scan extension path '{}': {}",
122					path.display(),
123					error
124				);
125			},
126		}
127	}
128
129	// Store discovered extensions into ApplicationState
130	let post_write_count = {
131		let mut Guard = _State
132			.ScannedExtensions
133			.ScannedExtensions
134			.lock()
135			.map_err(|Error| CommonError::StateLockPoisoned { Context:Error.to_string() })?;
136		Guard.clear();
137		for (Key, Dto) in &all_found_extensions {
138			Guard.insert(Key.clone(), Dto.clone());
139		}
140		Guard.len()
141	};
142
143	dev_log!(
144		"extensions",
145		"[ExtensionScanner] Extension scan complete. Found {} extensions ({} successful scans, {} failed scans). \
146		 ScannedExtensions map now has {} entries.",
147		all_found_extensions.len(),
148		successful_scans,
149		failed_scans,
150		post_write_count
151	);
152
153	if failed_scans > 0 {
154		dev_log!(
155			"extensions",
156			"warn: [ExtensionScanner] {} extension paths failed to scan",
157			failed_scans
158		);
159	}
160
161	Ok(())
162}
163
164/// Robust extension scanning with comprehensive error handling.
165///
166/// # Arguments
167/// * `ApplicationHandle` - Tauri application handle for extension management
168/// * `State` - Reference to the application state
169///
170/// # Returns
171/// Result indicating success or CommonError on failure
172///
173/// # Behavior
174/// - Clears potentially corrupted extension state first
175/// - Performs the scan
176/// - Retries once on failure
177/// - Comprehensive error logging
178pub async fn ScanExtensionsWithRecovery(
179	ApplicationHandle:AppHandle,
180	State:&crate::ApplicationState::ExtensionState::State::State,
181) -> Result<(), CommonError> {
182	dev_log!(
183		"extensions",
184		"[ExtensionScanner] Starting robust extension scan with recovery..."
185	);
186
187	// Clear potentially corrupted extension state first
188	// Note: Would clear
189	// State.Extension.ScannedExtensions.Extension.ScannedExtensions
190
191	// Perform the scan
192	match ScanAndPopulateExtensions(ApplicationHandle.clone(), State).await {
193		Ok(()) => {
194			dev_log!("extensions", "[ExtensionScanner] Robust extension scan completed successfully");
195			Ok(())
196		},
197		Err(error) => {
198			dev_log!(
199				"extensions",
200				"error: [ExtensionScanner] Robust extension scan failed: {}",
201				error
202			);
203			// Attempt recovery by clearing state and retrying once
204			dev_log!(
205				"extensions",
206				"warn: [ExtensionScanner] Attempting recovery from extension scan failure..."
207			);
208
209			// Clear state again
210			// Note: Would clear
211			// State.Extension.ScannedExtensions.Extension.ScannedExtensions
212
213			// Retry the scan with a cloned handle
214			ScanAndPopulateExtensions(ApplicationHandle.clone(), State).await
215		},
216	}
217}