aboutsummaryrefslogtreecommitdiffstats
path: root/lua/mason-core/package/init.lua
diff options
context:
space:
mode:
Diffstat (limited to 'lua/mason-core/package/init.lua')
-rw-r--r--lua/mason-core/package/init.lua182
1 files changed, 182 insertions, 0 deletions
diff --git a/lua/mason-core/package/init.lua b/lua/mason-core/package/init.lua
new file mode 100644
index 00000000..09b0ebbf
--- /dev/null
+++ b/lua/mason-core/package/init.lua
@@ -0,0 +1,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