Skip to main content

Mountain/IPC/WindServiceHandlers/Extension/
ExtensionUninstall.rs

1//! `extensions:uninstall` IPC handler - removes the install directory,
2//! clears the registry entry, and notifies Cocoon + Wind. Symmetric with
3//! `ExtensionInstall`.
4
5use std::{path::PathBuf, sync::Arc};
6
7use serde_json::{Value, json};
8use tauri::{AppHandle, Emitter};
9
10use crate::{
11	ExtensionManagement::VsixInstaller,
12	IPC::WindServiceHandlers::Extension::NotifyCocoonDeltaExtensions::Fn as NotifyCocoonDeltaExtensions,
13	RunTime::ApplicationRunTime::ApplicationRunTime,
14	dev_log,
15};
16
17pub async fn Fn(
18	ApplicationHandle:AppHandle,
19
20	Runtime:Arc<ApplicationRunTime>,
21
22	Args:Vec<Value>,
23) -> Result<Value, String> {
24	let OTELStart = crate::IPC::DevLog::NowNano::Fn();
25
26	let Identifier = match Args.first().and_then(|Value| {
27		Value
28			.as_str()
29			.map(str::to_owned)
30			.or_else(|| Value.get("id").and_then(|Inner| Inner.as_str()).map(str::to_owned))
31	}) {
32		Some(Value) => Value,
33
34		None => {
35			dev_log!("extensions", "extensions:uninstall no-op: Arguments[0] missing identifier");
36
37			crate::otel_span!("extensions:uninstall:noop-missing-id", OTELStart);
38
39			return Ok(Value::Null);
40		},
41	};
42
43	let Descriptor = Runtime
44		.Environment
45		.ApplicationState
46		.Extension
47		.ScannedExtensions
48		.Get(&Identifier);
49
50	let InstallDirectory = Descriptor
51		.as_ref()
52		.and_then(|Description| Description.ExtensionLocation.get("path").and_then(|V| V.as_str()))
53		.map(PathBuf::from);
54
55	if let Some(Directory) = InstallDirectory.clone() {
56		let DirectoryForBlocking = Directory.clone();
57
58		tokio::task::spawn_blocking(move || VsixInstaller::UninstallExtension(&DirectoryForBlocking))
59			.await
60			.map_err(|Error| format!("extensions:uninstall join error: {}", Error))?
61			.map_err(|Error| format!("extensions:uninstall failed: {}", Error))?;
62	}
63
64	let RemovedDescriptor = Descriptor
65		.as_ref()
66		.map(|Description| serde_json::to_value(Description).unwrap_or(Value::Null))
67		.unwrap_or(Value::Null);
68
69	Runtime
70		.Environment
71		.ApplicationState
72		.Extension
73		.ScannedExtensions
74		.Remove(&Identifier);
75
76	if !RemovedDescriptor.is_null() {
77		NotifyCocoonDeltaExtensions(Vec::new(), vec![RemovedDescriptor]);
78	}
79
80	if let Err(Error) = ApplicationHandle.emit(
81		"sky://extensions/uninstalled",
82		json!({
83			"identifier": Identifier,
84			"location": InstallDirectory.as_ref().map(|Value| Value.to_string_lossy().to_string()),
85		}),
86	) {
87		dev_log!("extensions", "warn: failed to emit sky://extensions/uninstalled: {}", Error);
88	}
89
90	dev_log!("extensions", "extensions:uninstall succeeded: {}", Identifier);
91
92	crate::otel_span!(
93		"extensions:uninstall:ok",
94		OTELStart,
95		&[("extension.identifier", Identifier.as_str())]
96	);
97
98	Ok(Value::Bool(true))
99}