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