Mountain/IPC/WindServiceHandlers/Extension/
ExtensionInstall.rs1#![allow(non_snake_case)]
2use std::sync::Arc;
19
20use serde_json::{Value, json};
21use tauri::{AppHandle, Emitter};
22
23use crate::{
24 ExtensionManagement::VsixInstaller,
25 IPC::{
26 UriComponents::FromFilePath::Fn as UriFromFilePath,
27 WindServiceHandlers::Extension::{
28 NotifyCocoonDeltaExtensions::NotifyCocoonDeltaExtensions,
29 UserExtensionDirectory::UserExtensionDirectory,
30 VsixPathFromArgs::VsixPathFromArgs,
31 },
32 },
33 RunTime::ApplicationRunTime::ApplicationRunTime,
34 dev_log,
35};
36
37pub async fn ExtensionInstall(
38 ApplicationHandle:AppHandle,
39 Runtime:Arc<ApplicationRunTime>,
40 Args:Vec<Value>,
41) -> Result<Value, String> {
42 let OTELStart = crate::IPC::DevLog::NowNano::Fn();
43
44 let VsixPath = match VsixPathFromArgs(&Args) {
45 Some(Path) => Path,
46 None => {
47 dev_log!("extensions", "extensions:install no-op: Arguments[0] missing or non-file URI");
48 crate::otel_span!("extensions:install:noop-missing-arg", OTELStart);
49 return Ok(Value::Null);
50 },
51 };
52
53 if VsixPath.extension().and_then(|Value| Value.to_str()) != Some("vsix") {
54 dev_log!("extensions", "extensions:install no-op: {} is not a .vsix", VsixPath.display());
55 crate::otel_span!("extensions:install:noop-not-vsix", OTELStart);
56 return Ok(Value::Null);
57 }
58
59 let InstallRoot = UserExtensionDirectory();
60
61 let Outcome = tokio::task::spawn_blocking(move || VsixInstaller::InstallVsix(&VsixPath, &InstallRoot))
62 .await
63 .map_err(|Error| format!("extensions:install join error: {}", Error))?
64 .map_err(|Error| format!("extensions:install failed: {}", Error))?;
65
66 Runtime
67 .Environment
68 .ApplicationState
69 .Extension
70 .ScannedExtensions
71 .AddOrUpdate(Outcome.Identifier.clone(), Outcome.Description.clone());
72
73 let Descriptor = serde_json::to_value(&Outcome.Description).unwrap_or(Value::Null);
74
75 NotifyCocoonDeltaExtensions(vec![Descriptor.clone()], Vec::new());
76
77 if let Err(Error) = ApplicationHandle.emit(
78 "sky://extensions/installed",
79 json!({
80 "identifier": Outcome.Identifier,
81 "version": Outcome.Version,
82 "location": Outcome.InstalledAt.to_string_lossy(),
83 }),
84 ) {
85 dev_log!("extensions", "warn: failed to emit sky://extensions/installed: {}", Error);
86 }
87
88 dev_log!(
89 "extensions",
90 "extensions:install succeeded: {} v{} at {}",
91 Outcome.Identifier,
92 Outcome.Version,
93 Outcome.InstalledAt.display()
94 );
95
96 crate::otel_span!(
97 "extensions:install:ok",
98 OTELStart,
99 &[
100 ("extension.identifier", Outcome.Identifier.as_str()),
101 ("extension.version", Outcome.Version.as_str()),
102 ]
103 );
104
105 Ok(json!({
112 "type": 1,
113 "isBuiltin": false,
114 "identifier": { "id": Outcome.Identifier },
115 "manifest": Descriptor,
116 "location": UriFromFilePath(Outcome.InstalledAt.to_string_lossy()),
117 "targetPlatform": "undefined",
118 "isValid": true,
119 "validations": [],
120 "preRelease": false,
121 "isWorkspaceScoped": false,
122 "isMachineScoped": false,
123 "isApplicationScoped": false,
124 "publisherId": null,
125 "isPreReleaseVersion": false,
126 "hasPreReleaseVersion": false,
127 "private": false,
128 "updated": false,
129 "pinned": false,
130 "forceAutoUpdate": false,
131 "source": "vsix",
132 "size": 0,
133 }))
134}