DevelopmentNodeEnvironment_MicrosoftVSCodeDependency_22NodeVersion_Bundle_Clean_Debug_ElectronProfile_EsbuildCompiler_Mountain/IPC/WindServiceHandlers/Extensions/
ExtensionsGetInstalled.rs1#![allow(non_snake_case)]
2
3use std::{sync::Arc, time::Duration};
35
36use CommonLibrary::ExtensionManagement::ExtensionManagementService::ExtensionManagementService;
37use serde_json::{Value, json};
38
39use crate::{
40 IPC::UriComponents::Normalize::Fn as NormalizeUri,
41 RunTime::ApplicationRunTime::ApplicationRunTime,
42 dev_log,
43};
44
45const EXTENSION_TYPE_SYSTEM:u8 = 0;
46
47const EXTENSION_TYPE_USER:u8 = 1;
48
49const SCAN_WAIT_CAP_MS:u64 = 5000;
50
51pub async fn ExtensionsGetInstalled(RunTime:Arc<ApplicationRunTime>, Arguments:Vec<Value>) -> Result<Value, String> {
52 let TypeFilter:Option<u8> = Arguments.first().and_then(|V| V.as_u64()).map(|N| N as u8);
53
54 let mut Extensions = RunTime
55 .Environment
56 .GetExtensions()
57 .await
58 .map_err(|Error| format!("extensions:getInstalled failed: {}", Error))?;
59
60 if Extensions.is_empty() {
61 let ScanReady = RunTime.Environment.ApplicationState.Extension.ScanReady.clone();
62
63 let Notified = tokio::time::timeout(Duration::from_millis(SCAN_WAIT_CAP_MS), ScanReady.notified()).await;
64
65 Extensions = RunTime
66 .Environment
67 .GetExtensions()
68 .await
69 .map_err(|Error| format!("extensions:getInstalled failed: {}", Error))?;
70
71 match Notified {
72 Ok(()) => {
73 dev_log!(
74 "extensions",
75 "extensions:getInstalled: scan-ready signal received, {} entries available",
76 Extensions.len()
77 );
78 },
79 Err(_) => {
80 dev_log!(
81 "extensions",
82 "warn: extensions:getInstalled: scan-ready timed out after {}ms; {} entries available",
83 SCAN_WAIT_CAP_MS,
84 Extensions.len()
85 );
86 },
87 }
88 }
89
90 let Wrapped:Vec<Value> = Extensions
91 .into_iter()
92 .filter_map(|Manifest| {
93 let IsBuiltin = Manifest.get("isBuiltin").and_then(Value::as_bool).unwrap_or(true);
94 let ExtensionType = if IsBuiltin { EXTENSION_TYPE_SYSTEM } else { EXTENSION_TYPE_USER };
95
96 if let Some(Wanted) = TypeFilter
97 && Wanted != ExtensionType
98 {
99 return None;
100 }
101
102 let Publisher = Manifest
103 .get("publisher")
104 .and_then(Value::as_str)
105 .filter(|S| !S.is_empty())
106 .unwrap_or("unknown")
107 .to_string();
108 let Name = Manifest
109 .get("name")
110 .and_then(Value::as_str)
111 .filter(|S| !S.is_empty())
112 .unwrap_or("unknown")
113 .to_string();
114 let Id = format!("{}.{}", Publisher, Name);
115
116 let Location = NormalizeUri(Manifest.get("extensionLocation"));
117
118 let mut Manifest = match Manifest {
119 Value::Object(_) => Manifest,
120 _ => json!({}),
121 };
122 if let Value::Object(ref mut Map) = Manifest {
123 Map.insert("extensionLocation".to_string(), Location.clone());
124 Map.entry("publisher".to_string()).or_insert_with(|| json!(Publisher.clone()));
125 Map.entry("name".to_string()).or_insert_with(|| json!(Name.clone()));
126 Map.entry("version".to_string()).or_insert_with(|| json!("0.0.0"));
127 }
128
129 Some(json!({
130 "type": ExtensionType,
131 "isBuiltin": IsBuiltin,
132 "identifier": { "id": Id },
133 "manifest": Manifest,
134 "location": Location,
135 "targetPlatform": "undefined",
136 "isValid": true,
137 "validations": [],
138 "preRelease": false,
139 "isWorkspaceScoped": false,
140 "isMachineScoped": false,
141 "isApplicationScoped": false,
142 "publisherId": null,
143 "isPreReleaseVersion": false,
144 "hasPreReleaseVersion": false,
145 "private": false,
146 "updated": false,
147 "pinned": false,
148 "forceAutoUpdate": false,
149 "source": if IsBuiltin { "system" } else { "vsix" },
150 "size": 0,
151 }))
152 })
153 .collect();
154
155 dev_log!(
156 "extensions",
157 "extensions:getInstalled type={:?} returning {} ILocalExtension-shaped entries",
158 TypeFilter,
159 Wrapped.len()
160 );
161
162 Ok(json!(Wrapped))
163}