Skip to main content

Mountain/IPC/WindServiceHandlers/Extension/
ExtensionUninstall.rs

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