aboutsummaryrefslogtreecommitdiffstats
path: root/lua/mason-core/package/init.lua
blob: 09b0ebbf59c48abb639cc896e755c5b49cf3d2a7 (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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
local AbstractPackage = require "mason-core.package.AbstractPackage"
local InstallLocation = require "mason-core.installer.InstallLocation"
local InstallRunner = require "mason-core.installer.InstallRunner"
local Optional = require "mason-core.optional"
local Result = require "mason-core.result"
local UninstallRunner = require "mason-core.installer.UninstallRunner"
local _ = require "mason-core.functional"
local fs = require "mason-core.fs"
local path = require "mason-core.path"
local registry = require "mason-registry"
local platform = require "mason-core.platform"
local Semaphore = require("mason-core.async.control").Semaphore

---@class Package : AbstractPackage
---@field spec RegistryPackageSpec
---@field local_semaphore Semaphore
local Package = {}
Package.__index = Package
setmetatable(Package, { __index = AbstractPackage })

---@param package_identifier string
---@return string, string?
Package.Parse = function(package_identifier)
    local name, version = unpack(vim.split(package_identifier, "@"))
    return name, version
end

---@alias PackageLanguage string

---@type table<PackageLanguage, PackageLanguage>
Package.Lang = setmetatable({}, {
    __index = function(s, lang)
        s[lang] = lang
        return s[lang]
    end,
})

---@enum PackageCategory
Package.Cat = {
    Compiler = "Compiler",
    Runtime = "Runtime",
    DAP = "DAP",
    LSP = "LSP",
    Linter = "Linter",
    Formatter = "Formatter",
}

---@alias PackageLicense string

---@type table<PackageLicense, PackageLicense>
Package.License = setmetatable({}, {
    __index = function(s, license)
        s[license] = license
        return s[license]
    end,
})

---@class RegistryPackageSourceVersionOverride : RegistryPackageSource
---@field constraint string

---@class RegistryPackageSource
---@field id string PURL-compliant identifier.
---@field version_overrides? RegistryPackageSourceVersionOverride[]

---@class RegistryPackageSchemas
---@field lsp string?

---@class RegistryPackageDeprecation
---@field since string
---@field message string

---@alias RegistryPackageSpecSchema
--- | '"registry+v1"'

---@class RegistryPackageSpec
---@field schema RegistryPackageSpecSchema
---@field name string
---@field description string
---@field homepage string
---@field licenses string[]
---@field languages string[]
---@field categories string[]
---@field deprecation RegistryPackageDeprecation?
---@field source RegistryPackageSource
---@field schemas RegistryPackageSchemas?
---@field bin table<string, string>?
---@field share table<string, string>?
---@field opt table<string, string>?

---@param spec RegistryPackageSpec
local function validate_spec(spec)
    if platform.cached_features["nvim-0.11"] ~= 1 then
        return
    end
    vim.validate("schema", spec.schema, _.equals "registry+v1", "registry+v1")
    vim.validate("name", spec.name, "string")
    vim.validate("description", spec.description, "string")
    vim.validate("homepage", spec.homepage, "string")
    vim.validate("licenses", spec.licenses, "table")
    vim.validate("categories", spec.categories, "table")
    vim.validate("languages", spec.languages, "table")
    vim.validate("source", spec.source, "table")
    vim.validate("bin", spec.bin, { "table", "nil" })
    vim.validate("share", spec.share, { "table", "nil" })
end

---@param spec RegistryPackageSpec
function Package:new(spec)
    validate_spec(spec)
    ---@type Package
    local instance = AbstractPackage.new(self, spec)
    instance.local_semaphore = Semaphore:new(1)
    return instance
end

---@param opts? PackageInstallOpts
---@param callback? InstallRunnerCallback
---@return InstallHandle
function Package:install(opts, callback)
    opts = opts or {}
    assert(not self:is_installing(), "Package is already installing.")
    assert(not self:is_uninstalling(), "Package is uninstalling.")
    opts = vim.tbl_extend("force", self.DEFAULT_INSTALL_OPTS, opts or {})

    local handle = self:new_install_handle(opts.location)
    registry:emit("package:install:handle", handle)
    local runner = InstallRunner:new(handle, AbstractPackage.SEMAPHORE)

    runner:execute(opts, callback)

    return handle
end

---@param opts? PackageUninstallOpts
---@param callback? fun(success: boolean, error: any)
function Package:uninstall(opts, callback)
    opts = opts or {}
    assert(self:is_installed(opts.location), "Package is not installed.")
    assert(not self:is_uninstalling(), "Package is already uninstalling.")
    local handle = self:new_uninstall_handle(opts.location)
    registry:emit("package:uninstall:handle", handle)
    local runner = UninstallRunner:new(handle, AbstractPackage.SEMAPHORE)
    runner:execute(opts, callback)
    return handle
end

---@param location? InstallLocation
function Package:is_installed(location)
    location = location or InstallLocation.global()
    local ok, stat = pcall(vim.loop.fs_stat, location:package(self.name))
    if not ok or not stat then
        return false
    end
    return stat.type == "directory"
end

function Package:get_lsp_settings_schema()
    local schema_file = InstallLocation.global()
        :share(path.concat { "mason-schemas", "lsp", ("%s.json"):format(self.name) })
    if fs.sync.file_exists(schema_file) then
        return Result.pcall(vim.json.decode, fs.sync.read_file(schema_file), {
            luanil = { object = true, array = true },
        }):ok()
    end
    return Optional.empty()
end

function Package:get_aliases()
    return require("mason-registry").get_package_aliases(self.name)
end

---@async
---@private
function Package:acquire_permit()
    return self.local_semaphore:acquire()
end

function Package:__tostring()
    return ("Package(name=%s)"):format(self.name)
end

return Package