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
40 Runtime:Arc<ApplicationRunTime>,
41
42 Args:Vec<Value>,
43) -> Result<Value, String> {
44 let OTELStart = crate::IPC::DevLog::NowNano::Fn();
45
46 let VsixPath = match VsixPathFromArgs(&Args) {
47 Some(Path) => Path,
48
49 None => {
50 dev_log!("extensions", "extensions:install no-op: Arguments[0] missing or non-file URI");
51
52 crate::otel_span!("extensions:install:noop-missing-arg", OTELStart);
53
54 return Ok(Value::Null);
55 },
56 };
57
58 if VsixPath.extension().and_then(|Value| Value.to_str()) != Some("vsix") {
59 dev_log!("extensions", "extensions:install no-op: {} is not a .vsix", VsixPath.display());
60
61 crate::otel_span!("extensions:install:noop-not-vsix", OTELStart);
62
63 return Ok(Value::Null);
64 }
65
66 let InstallRoot = UserExtensionDirectory();
67
68 let Outcome = tokio::task::spawn_blocking(move || VsixInstaller::InstallVsix(&VsixPath, &InstallRoot))
69 .await
70 .map_err(|Error| format!("extensions:install join error: {}", Error))?
71 .map_err(|Error| format!("extensions:install failed: {}", Error))?;
72
73 Runtime
74 .Environment
75 .ApplicationState
76 .Extension
77 .ScannedExtensions
78 .AddOrUpdate(Outcome.Identifier.clone(), Outcome.Description.clone());
79
80 let Descriptor = serde_json::to_value(&Outcome.Description).unwrap_or(Value::Null);
81
82 NotifyCocoonDeltaExtensions(vec![Descriptor.clone()], Vec::new());
83
84 if let Err(Error) = ApplicationHandle.emit(
85 "sky://extensions/installed",
86 json!({
87 "identifier": Outcome.Identifier,
88 "version": Outcome.Version,
89 "location": Outcome.InstalledAt.to_string_lossy(),
90 }),
91 ) {
92 dev_log!("extensions", "warn: failed to emit sky://extensions/installed: {}", Error);
93 }
94
95 dev_log!(
96 "extensions",
97 "extensions:install succeeded: {} v{} at {}",
98 Outcome.Identifier,
99 Outcome.Version,
100 Outcome.InstalledAt.display()
101 );
102
103 crate::otel_span!(
104 "extensions:install:ok",
105 OTELStart,
106 &[
107 ("extension.identifier", Outcome.Identifier.as_str()),
108 ("extension.version", Outcome.Version.as_str()),
109 ]
110 );
111
112 Ok(json!({
119 "type": 1,
120 "isBuiltin": false,
121 "identifier": { "id": Outcome.Identifier },
122 "manifest": Descriptor,
123 "location": UriFromFilePath(Outcome.InstalledAt.to_string_lossy()),
124 "targetPlatform": "undefined",
125 "isValid": true,
126 "validations": [],
127 "preRelease": false,
128 "isWorkspaceScoped": false,
129 "isMachineScoped": false,
130 "isApplicationScoped": false,
131 "publisherId": null,
132 "isPreReleaseVersion": false,
133 "hasPreReleaseVersion": false,
134 "private": false,
135 "updated": false,
136 "pinned": false,
137 "forceAutoUpdate": false,
138 "source": "vsix",
139 "size": 0,
140 }))
141}