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