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
21	Runtime:Arc<ApplicationRunTime>,
22
23	Args:Vec<Value>,
24) -> Result<Value, String> {
25	let OTELStart = crate::IPC::DevLog::NowNano::Fn();
26
27	let Identifier = match Args.first().and_then(|Value| {
28		Value
29			.as_str()
30			.map(str::to_owned)
31			.or_else(|| Value.get("id").and_then(|Inner| Inner.as_str()).map(str::to_owned))
32	}) {
33		Some(Value) => Value,
34
35		None => {
36			dev_log!("extensions", "extensions:uninstall no-op: Arguments[0] missing identifier");
37
38			crate::otel_span!("extensions:uninstall:noop-missing-id", OTELStart);
39
40			return Ok(Value::Null);
41		},
42	};
43
44	let Descriptor = Runtime
45		.Environment
46		.ApplicationState
47		.Extension
48		.ScannedExtensions
49		.Get(&Identifier);
50
51	let InstallDirectory = Descriptor
52		.as_ref()
53		.and_then(|Description| Description.ExtensionLocation.get("path").and_then(|V| V.as_str()))
54		.map(PathBuf::from);
55
56	if let Some(Directory) = InstallDirectory.clone() {
57		let DirectoryForBlocking = Directory.clone();
58
59		tokio::task::spawn_blocking(move || VsixInstaller::UninstallExtension(&DirectoryForBlocking))
60			.await
61			.map_err(|Error| format!("extensions:uninstall join error: {}", Error))?
62			.map_err(|Error| format!("extensions:uninstall failed: {}", Error))?;
63	}
64
65	let RemovedDescriptor = Descriptor
66		.as_ref()
67		.map(|Description| serde_json::to_value(Description).unwrap_or(Value::Null))
68		.unwrap_or(Value::Null);
69
70	Runtime
71		.Environment
72		.ApplicationState
73		.Extension
74		.ScannedExtensions
75		.Remove(&Identifier);
76
77	if !RemovedDescriptor.is_null() {
78		NotifyCocoonDeltaExtensions(Vec::new(), vec![RemovedDescriptor]);
79	}
80
81	if let Err(Error) = ApplicationHandle.emit(
82		"sky://extensions/uninstalled",
83		json!({
84			"identifier": Identifier,
85			"location": InstallDirectory.as_ref().map(|Value| Value.to_string_lossy().to_string()),
86		}),
87	) {
88		dev_log!("extensions", "warn: failed to emit sky://extensions/uninstalled: {}", Error);
89	}
90
91	dev_log!("extensions", "extensions:uninstall succeeded: {}", Identifier);
92
93	crate::otel_span!(
94		"extensions:uninstall:ok",
95		OTELStart,
96		&[("extension.identifier", Identifier.as_str())]
97	);
98
99	Ok(Value::Bool(true))
100}