aboutsummaryrefslogtreecommitdiffstats
path: root/lua/mason-core/managers/github/client.lua
blob: 3ef307c1100dcf05ac8ee1057a71833ce7e6d3b8 (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
local _ = require "mason-core.functional"
local log = require "mason-core.log"
local fetch = require "mason-core.fetch"
local spawn = require "mason-core.spawn"
local api = require "mason-registry.api"

local M = {}

---@alias GitHubReleaseAsset {url: string, id: integer, name: string, browser_download_url: string, created_at: string, updated_at: string, size: integer, download_count: integer}
---@alias GitHubRelease {tag_name: string, prerelease: boolean, draft: boolean, assets:GitHubReleaseAsset[]}
---@alias GitHubTag {name: string}
---@alias GitHubCommit {sha: string}

local stringify_params = _.compose(_.join "&", _.map(_.join "="), _.sort_by(_.head), _.to_pairs)

---@param path string
---@param opts { params: table<string, any>? }?
---@return Result # JSON decoded response.
local function gh_api_call(path, opts)
    if opts and opts.params then
        local params = stringify_params(opts.params)
        path = ("%s?%s"):format(path, params)
    end
    return spawn
        .gh({ "api", path, env = { CLICOLOR_FORCE = 0 } })
        :map(_.prop "stdout")
        :recover_catching(function()
            return fetch(("https://api.github.com/%s"):format(path), {
                headers = {
                    Accept = "application/vnd.github.v3+json; q=1.0, application/json; q=0.8",
                },
            }):get_or_throw()
        end)
        :map_catching(vim.json.decode)
end

M.api_call = gh_api_call

---@async
---@param repo string The GitHub repo ("username/repo").
---@return Result # Result<GitHubRelease[]>
function M.fetch_releases(repo)
    log.fmt_trace("Fetching GitHub releases for repo=%s", repo)
    local path = ("repos/%s/releases"):format(repo)
    return gh_api_call(path):map_err(function()
        return ("Failed to fetch releases for GitHub repository %s."):format(repo)
    end)
end

---@async
---@param repo string The GitHub repo ("username/repo").
---@param tag_name string The tag_name of the release to fetch.
function M.fetch_release(repo, tag_name)
    log.fmt_trace("Fetching GitHub release for repo=%s, tag_name=%s", repo, tag_name)
    local path = ("repos/%s/releases/tags/%s"):format(repo, tag_name)
    return gh_api_call(path):map_err(function()
        return ("Failed to fetch release %q for GitHub repository %s."):format(tag_name, repo)
    end)
end

---@alias FetchLatestGithubReleaseOpts {include_prerelease: boolean}

---@async
---@param repo string The GitHub repo ("username/repo").
---@param opts FetchLatestGithubReleaseOpts?
---@return Result # Result<GitHubRelease>
function M.fetch_latest_release(repo, opts)
    opts = opts or { include_prerelease = false }
    return api.repo.releases.latest({ repo = repo }, {
        params = {
            include_prerelease = opts.include_prerelease and "true" or "false",
        },
    })
end

---@async
---@param repo string The GitHub repo ("username/repo").
---@return Result # Result<GitHubTag[]>
function M.fetch_tags(repo)
    local path = ("repos/%s/tags"):format(repo)
    return gh_api_call(path):map_err(function()
        return ("Failed to fetch tags for GitHub repository %s."):format(repo)
    end)
end

---@async
---@param repo string The GitHub repo ("username/repo").
---@return Result # Result<string> The latest tag name.
function M.fetch_latest_tag(repo)
    return api.repo.tags.latest({ repo = repo }):map(_.prop "tag")
end

---@async
---@param repo string The GitHub repo ("username/repo").
---@param opts { page: integer?, per_page: integer? }?
---@return Result # Result<GitHubCommit[]>
function M.fetch_commits(repo, opts)
    local path = ("repos/%s/commits"):format(repo)
    return gh_api_call(path, {
        params = {
            page = opts and opts.page or 1,
            per_page = opts and opts.per_page or 30,
        },
    }):map_err(function()
        return ("Failed to fetch commits for GitHub repository %s."):format(repo)
    end)
end

---@alias GitHubRateLimit {limit: integer, remaining: integer, reset: integer, used: integer}
---@alias GitHubRateLimitResponse {resources: { core: GitHubRateLimit }}

---@async
--@return Result @of GitHubRateLimitResponse
function M.fetch_rate_limit()
    return gh_api_call "rate_limit"
end

return M