Mountain/ApplicationState/Internal/ExtensionScanner/
ScanAndPopulateExtensions.rs1use 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
45pub 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 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 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
179pub 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 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 dev_log!(
224 "extensions",
225 "warn: [ExtensionScanner] Attempting recovery from extension scan failure..."
226 );
227
228 ScanAndPopulateExtensions(ApplicationHandle.clone(), State).await
234 },
235 }
236}