aboutsummaryrefslogtreecommitdiffstats
path: root/lua/mason-core/fetch.lua
blob: 3bb7a214ca3c9abba5629bb741f6db1d480871fe (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
local log = require "mason-core.log"
local platform = require "mason-core.platform"
local Result = require "mason-core.result"
local spawn = require "mason-core.spawn"
local powershell = require "mason-core.managers.powershell"
local _ = require "mason-core.functional"

local USER_AGENT = "mason.nvim (+https://github.com/williamboman/mason.nvim)"

---@alias FetchMethod
---| '"GET"'
---| '"POST"'
---| '"PUT"'
---| '"PATCH"'
---| '"DELETE"'

---@alias FetchOpts {out_file: string?, method: FetchMethod?, headers: table<string, string>?, data: string?}

---@async
---@param url string The url to fetch.
---@param opts FetchOpts?
---@return Result # Result<string>
local function fetch(url, opts)
    opts = opts or {}
    if not opts.headers then
        opts.headers = {}
    end
    if not opts.method then
        opts.method = "GET"
    end
    opts.headers["User-Agent"] = USER_AGENT
    log.fmt_debug("Fetching URL %s", url)

    local platform_specific = Result.failure()

    if platform.is_win then
        local header_entries = _.join(
            ", ",
            _.map(function(pair)
                return ("%q = %q"):format(pair[1], pair[2])
            end, _.to_pairs(opts.headers))
        )
        local headers = ("@{%s}"):format(header_entries)
        if opts.out_file then
            platform_specific = powershell.command(
                ([[iwr %s -UseBasicParsing -Method %q -Uri %q %s -OutFile %q;]]):format(
                    headers,
                    opts.method,
                    url,
                    opts.data and ("-Body %s"):format(opts.data) or "",
                    opts.out_file
                )
            )
        else
            platform_specific = powershell.command(
                ([[Write-Output (iwr %s -Method %q -UseBasicParsing %s -Uri %q).Content;]]):format(
                    headers,
                    opts.method,
                    opts.data and ("-Body %s"):format(opts.data) or "",
                    url
                )
            )
        end
    end

    return platform_specific
        :recover_catching(function()
            local headers =
                _.sort_by(_.identity, _.map(_.compose(_.format "--header='%s'", _.join ": "), _.to_pairs(opts.headers)))
            return spawn
                .wget({
                    headers,
                    "-nv",
                    "-O",
                    opts.out_file or "-",
                    ("--method=%s"):format(opts.method),
                    opts.data and {
                        ("--body-data=%s"):format(opts.data) or vim.NIL,
                    } or vim.NIL,
                    url,
                })
                :get_or_throw()
        end)
        :recover_catching(function()
            local headers = _.sort_by(
                _.nth(2),
                _.map(
                    _.compose(function(header)
                        return { "-H", header }
                    end, _.join ": "),
                    _.to_pairs(opts.headers)
                )
            )
            return spawn
                .curl({
                    headers,
                    "-fsSL",
                    {
                        "-X",
                        opts.method,
                    },
                    opts.data and { "-d", "@-" } or vim.NIL,
                    opts.out_file and { "-o", opts.out_file } or vim.NIL,
                    url,
                    on_spawn = function(_, stdio)
                        local stdin = stdio[1]
                        if opts.data then
                            log.trace("Writing stdin to curl", opts.data)
                            stdin:write(opts.data)
                        end
                        stdin:close()
                    end,
                })
                :get_or_throw()
        end)
        :map(function(result)
            if opts.out_file then
                return result
            else
                return result.stdout
            end
        end)
end

return fetch