Skip to main content

DevelopmentNodeEnvironment_MicrosoftVSCodeDependency_22NodeVersion_Bundle_Clean_Debug_ElectronProfile_EsbuildCompiler_Mountain/ApplicationState/Internal/ExtensionScanner/
ScanAndPopulateExtensions.rs

1use std::{collections::HashMap, path::PathBuf};
2
3use CommonLibrary::Error::CommonError::CommonError;
4use serde_json::Value;
5use tauri::AppHandle;
6
7use crate::{
8	ApplicationState::DTO::ExtensionDescriptionStateDTO::ExtensionDescriptionStateDTO,
9	ExtensionManagement,
10	dev_log,
11};
12
13pub async fn ScanAndPopulateExtensions(
14	ApplicationHandle:AppHandle,
15
16	_State:&crate::ApplicationState::State::ExtensionState::State::State,
17) -> Result<(), CommonError> {
18	dev_log!("extensions", "[ExtensionScanner] Starting extension scan...");
19
20	let ScanPaths:Vec<PathBuf> = _State.Registry.GetExtensionScanPaths();
21
22	dev_log!(
23		"extensions",
24		"[ExtensionScanner] Scanning {} paths in parallel",
25		ScanPaths.len()
26	);
27
28	// Scan all paths concurrently; each spawns its own tokio task so slow
29	// directories (e.g. a network-mounted extensions folder) don't stall the
30	// others.
31	let Futures:Vec<_> = ScanPaths
32		.into_iter()
33		.map(|Path| {
34			let Handle = ApplicationHandle.clone();
35
36			async move {
37				let Display = Path.display().to_string();
38
39				match ExtensionManagement::Scanner::ScanDirectoryForExtensions(Handle, Path).await {
40					Ok(Found) => {
41						dev_log!(
42							"extensions",
43							"[ExtensionScanner] Path '{}' → {} extensions",
44							Display,
45							Found.len()
46						);
47
48						(Display, Ok(Found))
49					},
50
51					Err(E) => {
52						dev_log!("extensions", "warn: [ExtensionScanner] Path '{}' failed: {}", Display, E);
53
54						(Display, Err(E))
55					},
56				}
57			}
58		})
59		.collect();
60
61	let Results = futures::future::join_all(Futures).await;
62
63	let mut All:HashMap<String, ExtensionDescriptionStateDTO> = HashMap::new();
64
65	let mut SuccessfulScans = 0usize;
66
67	let mut FailedScans = 0usize;
68
69	for (_Path, Result) in Results {
70		match Result {
71			Ok(Found) => {
72				SuccessfulScans += 1;
73
74				for Extension in Found {
75					let Identifier = Extension
76						.Identifier
77						.get("value")
78						.and_then(Value::as_str)
79						.unwrap_or_default()
80						.to_string();
81
82					if !Identifier.is_empty() {
83						All.insert(Identifier, Extension);
84					}
85				}
86			},
87
88			Err(_) => {
89				FailedScans += 1;
90			},
91		}
92	}
93
94	let PostWriteCount = {
95		let mut Guard = _State
96			.ScannedExtensions
97			.ScannedExtensions
98			.lock()
99			.map_err(|Error| CommonError::StateLockPoisoned { Context:Error.to_string() })?;
100
101		Guard.clear();
102
103		for (Key, Dto) in &All {
104			Guard.insert(Key.clone(), Dto.clone());
105		}
106
107		Guard.len()
108	};
109
110	dev_log!(
111		"extensions",
112		"[ExtensionScanner] Complete: {} extensions ({} paths ok, {} failed). State has {} entries.",
113		All.len(),
114		SuccessfulScans,
115		FailedScans,
116		PostWriteCount
117	);
118
119	// Unblock any callers waiting for the first scan result.
120	_State.ScanReady.notify_waiters();
121
122	Ok(())
123}
124
125/// Robust extension scanning - clears state first, retries once on failure.
126pub async fn ScanExtensionsWithRecovery(
127	ApplicationHandle:AppHandle,
128
129	State:&crate::ApplicationState::State::ExtensionState::State::State,
130) -> Result<(), CommonError> {
131	dev_log!("extensions", "[ExtensionScanner] Starting robust extension scan...");
132
133	match ScanAndPopulateExtensions(ApplicationHandle.clone(), State).await {
134		Ok(()) => {
135			dev_log!("extensions", "[ExtensionScanner] Robust scan completed successfully");
136
137			Ok(())
138		},
139
140		Err(Error) => {
141			dev_log!("extensions", "error: [ExtensionScanner] Scan failed: {}; retrying once", Error);
142
143			ScanAndPopulateExtensions(ApplicationHandle, State).await
144		},
145	}
146}