aboutsummaryrefslogtreecommitdiffstats
path: root/lua/mason-core/installer/UninstallRunner.lua
diff options
context:
space:
mode:
authorWilliam Boman <william@redwill.se>2023-11-07 00:29:18 +0100
committerWilliam Boman <william@redwill.se>2025-02-19 12:15:48 +0100
commit6a7662760c515c74f2c37fc825776ead65d307f9 (patch)
tree0f4496d0678c7029b10236cbf48cc0f5ff63c1dc /lua/mason-core/installer/UninstallRunner.lua
parentfix(pypi): remove -U flag and fix log message (diff)
downloadmason-6a7662760c515c74f2c37fc825776ead65d307f9.tar
mason-6a7662760c515c74f2c37fc825776ead65d307f9.tar.gz
mason-6a7662760c515c74f2c37fc825776ead65d307f9.tar.bz2
mason-6a7662760c515c74f2c37fc825776ead65d307f9.tar.lz
mason-6a7662760c515c74f2c37fc825776ead65d307f9.tar.xz
mason-6a7662760c515c74f2c37fc825776ead65d307f9.tar.zst
mason-6a7662760c515c74f2c37fc825776ead65d307f9.zip
refactor!: change Package API
This changes the following public APIs: **(_breaking_) Events on the `Package` class** The `uninstall:success` event on the `Package` class now receives an `InstallReceipt` as argument, instead of an `InstallHandle`. This receipt is an in-memory representation of what was uninstalled. There's also a new `uninstall:failed` event for situations where uninstallation for some reason fails. Note: this also applies to the registry events (i.e. `package:uninstall:success` and `package:uninstall:failed`). --- **(_breaking_) `Package:uninstall()` is now asynchronous and receives two new arguments, similarly to `Package:install()`** While package uninstallations remain synchronous under the hood, the public API has been changed from synchronous -> asynchronous. Users of this method are recommended to provide a callback in situations where code needs to execute after uninstallation fully completes. --- **(_breaking_) `Package:get_install_path()` has been removed. --- **`Package:install()` now takes an optional callback** This callback allows consumers to be informed whether installation was successful or not without having to go through a different, low-level, API. See below for a comparison between the old and new APIs: ```lua -- before local handle = pkg:install() handle:once("closed", function () -- ... end) -- after pkg:install({}, function (success, result) -- ... end) ```
Diffstat (limited to 'lua/mason-core/installer/UninstallRunner.lua')
-rw-r--r--lua/mason-core/installer/UninstallRunner.lua119
1 files changed, 119 insertions, 0 deletions
diff --git a/lua/mason-core/installer/UninstallRunner.lua b/lua/mason-core/installer/UninstallRunner.lua
new file mode 100644
index 00000000..661bfefa
--- /dev/null
+++ b/lua/mason-core/installer/UninstallRunner.lua
@@ -0,0 +1,119 @@
+local InstallContext = require "mason-core.installer.context"
+local Result = require "mason-core.result"
+local _ = require "mason-core.functional"
+local a = require "mason-core.async"
+local compiler = require "mason-core.installer.compiler"
+local control = require "mason-core.async.control"
+local fs = require "mason-core.fs"
+local log = require "mason-core.log"
+local registry = require "mason-registry"
+
+local OneShotChannel = control.OneShotChannel
+
+---@class UninstallRunner
+---@field handle InstallHandle
+---@field global_semaphore Semaphore
+---@field package_permit Permit?
+---@field global_permit Permit?
+local UninstallRunner = {}
+UninstallRunner.__index = UninstallRunner
+
+---@param handle InstallHandle
+---@param global_semaphore Semaphore
+---@return UninstallRunner
+function UninstallRunner:new(handle, global_semaphore)
+ local instance = {}
+ setmetatable(instance, self)
+ instance.handle = handle
+ instance.global_semaphore = global_semaphore
+ return instance
+end
+
+---@param opts PackageUninstallOpts
+---@param callback? InstallRunnerCallback
+function UninstallRunner:execute(opts, callback)
+ local pkg = self.handle.package
+ local location = self.handle.location
+ log.fmt_info("Executing uninstaller for %s %s", pkg, opts)
+ a.run(function()
+ Result.try(function(try)
+ if not opts.bypass_permit then
+ try(self:acquire_permit()):receive()
+ end
+ ---@type InstallReceipt?
+ local receipt = pkg:get_receipt(location):or_else(nil)
+ if receipt == nil then
+ log.fmt_warn("Receipt not found when uninstalling %s", pkg)
+ end
+ try(pkg:unlink(location))
+ fs.sync.rmrf(location:package(pkg.name))
+ return receipt
+ end):get_or_throw()
+ end, function(success, result)
+ if not self.handle:is_closing() then
+ self.handle:close()
+ end
+ self:release_permit()
+
+ if success then
+ local receipt = result
+ log.fmt_info("Uninstallation succeeded for %s", pkg)
+ if callback then
+ callback(true, receipt)
+ end
+ pkg:emit("uninstall:success", receipt)
+ registry:emit("package:uninstall:success", pkg, receipt)
+ else
+ log.fmt_error("Uninstallation failed for %s error=%s", pkg, result)
+ if callback then
+ callback(false, result)
+ end
+ pkg:emit("uninstall:failed", result)
+ registry:emit("package:uninstall:failed", pkg, result)
+ end
+ end)
+end
+
+---@private
+function UninstallRunner:acquire_permit()
+ local channel = OneShotChannel:new()
+ log.fmt_debug("Acquiring permit for %s", self.handle.package)
+ local handle = self.handle
+ if handle:is_active() or handle:is_closing() then
+ log.fmt_debug("Received active or closing handle %s", handle)
+ return Result.failure "Invalid handle state."
+ end
+
+ handle:queued()
+ a.run(function()
+ self.global_permit = self.global_semaphore:acquire()
+ self.package_permit = handle.package:acquire_permit()
+ end, function(success, err)
+ if not success or handle:is_closing() then
+ if not success then
+ log.error("Acquiring permits failed", err)
+ end
+ self:release_permit()
+ else
+ log.fmt_debug("Activating handle %s", handle)
+ handle:active()
+ channel:send()
+ end
+ end)
+
+ return Result.success(channel)
+end
+
+---@private
+function UninstallRunner:release_permit()
+ if self.global_permit then
+ self.global_permit:forget()
+ self.global_permit = nil
+ end
+ if self.package_permit then
+ self.package_permit:forget()
+ self.package_permit = nil
+ end
+end
+
+return UninstallRunner