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