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 _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 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 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
164pub 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 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 dev_log!(
205 "extensions",
206 "warn: [ExtensionScanner] Attempting recovery from extension scan failure..."
207 );
208
209 ScanAndPopulateExtensions(ApplicationHandle.clone(), State).await
215 },
216 }
217}