aboutsummaryrefslogtreecommitdiffstats
path: root/lua/mason-core/managers/cargo/init.lua
diff options
context:
space:
mode:
authorWilliam Boman <william@redwill.se>2022-10-05 20:33:21 +0200
committerGitHub <noreply@github.com>2022-10-05 20:33:21 +0200
commit738684097dfdd9a4a67cd217b0beda3e169bd85d (patch)
treed2eb8485dedcac66061b0d99ad7f810828911073 /lua/mason-core/managers/cargo/init.lua
parenttest(cargo): stub crates.io http call (#508) (diff)
downloadmason-738684097dfdd9a4a67cd217b0beda3e169bd85d.tar
mason-738684097dfdd9a4a67cd217b0beda3e169bd85d.tar.gz
mason-738684097dfdd9a4a67cd217b0beda3e169bd85d.tar.bz2
mason-738684097dfdd9a4a67cd217b0beda3e169bd85d.tar.lz
mason-738684097dfdd9a4a67cd217b0beda3e169bd85d.tar.xz
mason-738684097dfdd9a4a67cd217b0beda3e169bd85d.tar.zst
mason-738684097dfdd9a4a67cd217b0beda3e169bd85d.zip
feat(cargo): improve handling of git-based crates (#512)
This is all pretty overkill, especially considering the small amount of packages based on git-based crates.
Diffstat (limited to 'lua/mason-core/managers/cargo/init.lua')
-rw-r--r--lua/mason-core/managers/cargo/init.lua167
1 files changed, 118 insertions, 49 deletions
diff --git a/lua/mason-core/managers/cargo/init.lua b/lua/mason-core/managers/cargo/init.lua
index b8cd4fa7..8890daf6 100644
--- a/lua/mason-core/managers/cargo/init.lua
+++ b/lua/mason-core/managers/cargo/init.lua
@@ -5,6 +5,8 @@ local a = require "mason-core.async"
local Optional = require "mason-core.optional"
local installer = require "mason-core.installer"
local client = require "mason-core.managers.cargo.client"
+local github = require "mason-core.managers.github"
+local github_client = require "mason-core.managers.github.client"
local _ = require "mason-core.functional"
local get_bin_path = _.compose(path.concat, function(executable)
@@ -23,32 +25,50 @@ local M = {}
---@async
---@param crate string The crate to install.
----@param opts {git: boolean | string, features: string?, bin: string[]? }?
+---@param opts { git: { url: string, tag: boolean? }, features: string?, bin: string[]? }?
function M.crate(crate, opts)
return function()
- M.install(crate, opts).with_receipt()
+ if opts and opts.git and opts.git.tag then
+ local ctx = installer.context()
+ local repo = assert(opts.git.url:match "^https://github%.com/(.+)$", "git url needs to be github.com")
+ local source = github.tag { repo = repo }
+ source.with_receipt()
+ ctx.requested_version = Optional.of(source.tag)
+ M.install(crate, opts)
+ else
+ M.install(crate, opts).with_receipt()
+ end
end
end
---@async
---@param crate string The crate to install.
----@param opts {git: boolean | string, features: string?, bin: string[]? }?
+---@param opts { git: { url: string, tag: boolean? }, features: string?, bin: string[]? }?
function M.install(crate, opts)
local ctx = installer.context()
opts = opts or {}
- ctx.requested_version:if_present(function()
- assert(not opts.git, "Providing a version when installing a git crate is not allowed.")
- end)
- ---@type string | string[]
- local final_crate = crate
+ local version
if opts.git then
- final_crate = { "--git" }
- if type(opts.git) == "string" then
- table.insert(final_crate, opts.git)
+ if opts.git.tag then
+ assert(ctx.requested_version:is_present(), "version is required when installing tagged git crate.")
end
- table.insert(final_crate, crate)
+ version = ctx.requested_version
+ :map(function(version)
+ if opts.git.tag then
+ return { "--tag", version }
+ else
+ return { "--rev", version }
+ end
+ end)
+ :or_else(vim.NIL)
+ else
+ version = ctx.requested_version
+ :map(function(version)
+ return { "--version", version }
+ end)
+ :or_else(vim.NIL)
end
ctx.spawn.cargo {
@@ -56,13 +76,10 @@ function M.install(crate, opts)
"--root",
".",
"--locked",
- ctx.requested_version
- :map(function(version)
- return { "--version", version }
- end)
- :or_else(vim.NIL),
+ version,
+ opts.git and { "--git", opts.git.url } or vim.NIL,
opts.features and { "--features", opts.features } or vim.NIL,
- final_crate,
+ crate,
}
if opts.bin then
@@ -76,42 +93,39 @@ function M.install(crate, opts)
}
end
+---@alias InstalledCrate { name: string, version: string, github_ref: { owner: string, repo: string, ref: string }? }
+
+---@param line string
+---@return InstalledCrate? crate
+local function parse_installed_crate(line)
+ local name, version, context = line:match "^(.+)%s+v([^%s:]+) ?(.*):$"
+ if context then
+ local owner, repo, ref = context:match "^%(https://github%.com/(.+)/([^?]+).*#(.+)%)$"
+ if ref then
+ return { name = name, version = ref, github_ref = { owner = owner, repo = repo, ref = ref } }
+ end
+ end
+ if name and version then
+ return { name = name, version = version }
+ end
+end
+
---@param output string The `cargo install --list` output.
----@return table<string, string> # Key is the crate name, value is its version.
+---@return table<string, InstalledCrate> # Key is the crate name, value is its version.
function M.parse_installed_crates(output)
local installed_crates = {}
for _, line in ipairs(vim.split(output, "\n")) do
- local name, version = line:match "^(.+)%s+v([.%S]+)[%s:]"
- if name and version then
- installed_crates[name] = version
+ local installed_crate = parse_installed_crate(line)
+ if installed_crate then
+ installed_crates[installed_crate.name] = installed_crate
end
end
return installed_crates
end
---@async
----@param receipt InstallReceipt<InstallReceiptPackageSource>
---@param install_dir string
-function M.check_outdated_primary_package(receipt, install_dir)
- return M.get_installed_primary_package_version(receipt, install_dir):map_catching(function(installed_version)
- ---@type CrateResponse
- local crate_response = client.fetch_crate(receipt.primary_source.package):get_or_throw()
- if installed_version ~= crate_response.crate.max_stable_version then
- return {
- name = receipt.primary_source.package,
- current_version = installed_version,
- latest_version = crate_response.crate.max_stable_version,
- }
- else
- error "Primary package is not outdated."
- end
- end)
-end
-
----@async
----@param receipt InstallReceipt<InstallReceiptPackageSource>
----@param install_dir string
-function M.get_installed_primary_package_version(receipt, install_dir)
+local function get_installed_crates(install_dir)
return spawn
.cargo({
"install",
@@ -121,13 +135,68 @@ function M.get_installed_primary_package_version(receipt, install_dir)
cwd = install_dir,
})
:map_catching(function(result)
- local installed_crates = M.parse_installed_crates(result.stdout)
- if vim.in_fast_event() then
- a.scheduler() -- needed because vim.fn.* call
- end
- local pkg = vim.fn.fnamemodify(receipt.primary_source.package, ":t")
- return Optional.of_nilable(installed_crates[pkg]):or_else_throw "Failed to find cargo package version."
+ return M.parse_installed_crates(result.stdout)
end)
end
+---@async
+---@param receipt InstallReceipt<InstallReceiptPackageSource>
+---@param install_dir string
+function M.check_outdated_primary_package(receipt, install_dir)
+ if vim.in_fast_event() then
+ a.scheduler()
+ end
+ local crate_name = vim.fn.fnamemodify(receipt.primary_source.package, ":t")
+ return get_installed_crates(install_dir)
+ :ok()
+ :map(_.prop(crate_name))
+ :map(
+ ---@param installed_crate InstalledCrate
+ function(installed_crate)
+ if installed_crate.github_ref then
+ ---@type GitHubCommit
+ local latest_commit = github_client
+ .fetch_commits(
+ ("%s/%s"):format(installed_crate.github_ref.owner, installed_crate.github_ref.repo),
+ { page = 1, per_page = 1 }
+ )
+ :get_or_throw("Failed to fetch latest commits.")[1]
+ if not vim.startswith(latest_commit.sha, installed_crate.github_ref.ref) then
+ return {
+ name = receipt.primary_source.package,
+ current_version = installed_crate.github_ref.ref,
+ latest_version = latest_commit.sha,
+ }
+ end
+ else
+ ---@type CrateResponse
+ local crate_response = client.fetch_crate(crate_name):get_or_throw()
+ if installed_crate.version ~= crate_response.crate.max_stable_version then
+ return {
+ name = receipt.primary_source.package,
+ current_version = installed_crate.version,
+ latest_version = crate_response.crate.max_stable_version,
+ }
+ end
+ end
+ end
+ )
+ :ok_or(_.always "Primary package is not outdated.")
+end
+
+---@async
+---@param receipt InstallReceipt<InstallReceiptPackageSource>
+---@param install_dir string
+function M.get_installed_primary_package_version(receipt, install_dir)
+ return get_installed_crates(install_dir):map(function(pkgs)
+ if vim.in_fast_event() then
+ a.scheduler()
+ end
+ local pkg = vim.fn.fnamemodify(receipt.primary_source.package, ":t")
+ return Optional.of_nilable(pkgs[pkg])
+ :map(_.prop "version")
+ :or_else_throw "Failed to find cargo package version."
+ end)
+end
+
return M