aboutsummaryrefslogtreecommitdiffstats
path: root/lua/mason-core/installer/UninstallRunner.lua
blob: cff4c9cd16b51bc043986c5b24531e800cac347d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
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()
        return 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)
            else
                try(pkg:unlink(location))
            end
            fs.sync.rmrf(pkg:get_install_path(location))
            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, opts)
            registry:emit(
                pkg.spec.system and "system-package:uninstall:success" or "package:uninstall:success",
                pkg,
                receipt,
                opts
            )
        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(
                pkg.spec.system and "system-package:uninstall:success" or "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