Skip to content

Commit 260f558

Browse files
committed
feat: add type annotations for plenary.curl
1 parent a94cace commit 260f558

File tree

3 files changed

+102
-5
lines changed

3 files changed

+102
-5
lines changed

lua/plenary/curl.lua

Lines changed: 100 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
-- luacheck: push ignore 631
2+
13
--[[
24
Curl Wrapper
35
@@ -28,6 +30,9 @@ see test/plenary/curl_spec.lua for examples.
2830
author = github.com/tami5
2931
]]
3032
--
33+
---@alias PlenaryCurlMethod fun(url: string|PlenaryCurlOptions, opts?: PlenaryCurlOptions): PlenaryCurlResponse|PlenaryJob|string[]
34+
35+
-- luacheck: pop
3136

3237
local util, parse = {}, {}
3338

@@ -40,6 +45,8 @@ local P = require "plenary.path"
4045
-- Utils ----------------------------------------------------
4146
-------------------------------------------------------------
4247

48+
---@param str string|integer
49+
---@return string|integer
4350
util.url_encode = function(str)
4451
if type(str) ~= "number" then
4552
str = str:gsub("\r?\n", "\r\n")
@@ -53,12 +60,20 @@ util.url_encode = function(str)
5360
end
5461
end
5562

63+
---@param kv table<string, string>
64+
---@param prefix string
65+
---@param sep string
66+
---@return string[]
5667
util.kv_to_list = function(kv, prefix, sep)
5768
return vim.tbl_flatten(F.kv_map(function(kvp)
5869
return { prefix, kvp[1] .. sep .. kvp[2] }
5970
end, kv))
6071
end
6172

73+
---@param kv table<string, string>
74+
---@param sep? string
75+
---@param kvsep string
76+
---@return string
6277
util.kv_to_str = function(kv, sep, kvsep)
6378
return F.join(
6479
F.kv_map(function(kvp)
@@ -68,6 +83,7 @@ util.kv_to_str = function(kv, sep, kvsep)
6883
)
6984
end
7085

86+
---@return { [1]: string, [2]: string }
7187
util.gen_dump_path = function()
7288
local path
7389
local id = string.gsub("xxxx4xxx", "[xy]", function(l)
@@ -86,15 +102,21 @@ end
86102
-- Parsers ----------------------------------------------------
87103
---------------------------------------------------------------
88104

105+
---comment
106+
---@param t? table<string, string>
107+
---@return string[]?
89108
parse.headers = function(t)
90109
if not t then
91110
return
92111
end
112+
---@param str string
113+
---@return string
93114
local upper = function(str)
94115
return string.gsub(" " .. str, "%W%l", string.upper):sub(2)
95116
end
96117
return util.kv_to_list(
97118
(function()
119+
---@type table<string, string>
98120
local normilzed = {}
99121
for k, v in pairs(t) do
100122
normilzed[upper(k:gsub("_", "%-"))] = v
@@ -106,13 +128,17 @@ parse.headers = function(t)
106128
)
107129
end
108130

131+
---@param t? table<string, string>
132+
---@return string[]?
109133
parse.data_body = function(t)
110134
if not t then
111135
return
112136
end
113137
return util.kv_to_list(t, "-d", "=")
114138
end
115139

140+
---@param xs? string|table<string, string>
141+
---@return string[]?
116142
parse.raw_body = function(xs)
117143
if not xs then
118144
return
@@ -124,20 +150,26 @@ parse.raw_body = function(xs)
124150
end
125151
end
126152

153+
---@param t? table<string, string>
154+
---@return string[]?
127155
parse.form = function(t)
128156
if not t then
129157
return
130158
end
131159
return util.kv_to_list(t, "-F", "=")
132160
end
133161

162+
---@param t? table<string, string>
163+
---@return string?
134164
parse.curl_query = function(t)
135165
if not t then
136166
return
137167
end
138168
return util.kv_to_str(t, "&", "=")
139169
end
140170

171+
---@param s? string
172+
---@return { [1]: "-I"|"-X", [2]: string? }?
141173
parse.method = function(s)
142174
if not s then
143175
return
@@ -149,39 +181,50 @@ parse.method = function(s)
149181
end
150182
end
151183

184+
---@param p? string
185+
---@return { [1]: "-d", [2]: string }?
152186
parse.file = function(p)
153187
if not p then
154188
return
155189
end
156190
return { "-d", "@" .. P.expand(P.new(p)) }
157191
end
158192

193+
---@param xs string|table<string, string>
194+
---@return { [1]: "-u", [2]: string }?
159195
parse.auth = function(xs)
160196
if not xs then
161197
return
162198
end
163199
return { "-u", type(xs) == "table" and util.kv_to_str(xs, nil, ":") or xs }
164200
end
165201

202+
---@param xs string
203+
---@param q table<string, string>
204+
---@return string?
166205
parse.url = function(xs, q)
167206
if not xs then
168207
return
169208
end
170-
q = parse.curl_query(q)
209+
local query = parse.curl_query(q)
171210
if type(xs) == "string" then
172-
return q and xs .. "?" .. q or xs
211+
return query and xs .. "?" .. query or xs
173212
elseif type(xs) == "table" then
174213
error "Low level URL definition is not supported."
175214
end
176215
end
177216

217+
---@param s string?
218+
---@return { [1]: "-H", [2]: string }?
178219
parse.accept_header = function(s)
179220
if not s then
180221
return
181222
end
182223
return { "-H", "Accept: " .. s }
183224
end
184225

226+
---@param s string?
227+
---@return { [1]: string }?
185228
parse.http_version = function(s)
186229
if not s then
187230
return
@@ -197,9 +240,36 @@ end
197240

198241
-- Parse Request -------------------------------------------
199242
------------------------------------------------------------
243+
---@class PlenaryCurlOptions
244+
---@field auth? string|table<string, string> Basic request auth, 'user:pass', or {"user", "pass"}
245+
---@field body? string|string[] The request body
246+
---@field dry_run? boolean whether to return the args to be ran through curl.
247+
---@field form? table<string, string> request form
248+
---@field http_version? string HTTP version to use: 'HTTP/0.9', 'HTTP/1.0', 'HTTP/1.1', 'HTTP/2', or 'HTTP/3'
249+
---@field insecure? boolean Allow insecure server connections
250+
---@field output? string where to download something.
251+
---@field proxy? string [protocol://]host[:port] Use this proxy
252+
---@field query? table<string, string> url query, append after the url
253+
---@field raw? string[] any additonal curl args, it must be an array/list.
254+
---@field timeout? integer request timeout in mseconds
255+
---@field url? string The url to make the request to.
256+
---@field accept? string
257+
---@field callback? fun(response: PlenaryCurlResponse)
258+
---@field compressed? boolean
259+
---@field data? string[]
260+
---@field dump? string
261+
---@field headers? string[]
262+
---@field in_file? string
263+
---@field method? string
264+
---@field on_error? fun(err: { message: string, stderr: string, exit: integer })
265+
---@field raw_body? string
266+
---@field stream? PlenaryJobCallback
267+
268+
---@param opts PlenaryCurlOptions
269+
---@return string[] result, PlenaryCurlOptions opts
200270
parse.request = function(opts)
201271
if opts.body then
202-
local b = opts.body
272+
local b = opts.body --[[@as string|string[] ]]
203273
local silent_is_file = function()
204274
local status, result = pcall(P.is_file, P.new(b))
205275
return status and result
@@ -248,6 +318,16 @@ end
248318

249319
-- Parse response ------------------------------------------
250320
------------------------------------------------------------
321+
---@class PlenaryCurlResponse
322+
---@field status integer
323+
---@field headers string[]
324+
---@field body string
325+
---@field exit integer
326+
327+
---@param lines string[]
328+
---@param dump_path string
329+
---@param code integer
330+
---@return PlenaryCurlResponse
251331
parse.response = function(lines, dump_path, code)
252332
local headers = P.readlines(dump_path)
253333
local status = tonumber(string.match(headers[1], "([%w+]%d+)"))
@@ -264,6 +344,8 @@ parse.response = function(lines, dump_path, code)
264344
}
265345
end
266346

347+
---@param specs PlenaryCurlOptions
348+
---@return PlenaryCurlResponse|PlenaryJob|string[]
267349
local request = function(specs)
268350
local response = {}
269351
local args, opts = parse.request(vim.tbl_extend("force", {
@@ -276,6 +358,7 @@ local request = function(specs)
276358
return args
277359
end
278360

361+
---@type PlenaryJobOptions
279362
local job_opts = {
280363
command = "curl",
281364
args = args,
@@ -299,7 +382,7 @@ local request = function(specs)
299382
error(message)
300383
end
301384
end
302-
local output = parse.response(j:result(), opts.dump[2], code)
385+
local output = parse.response((j:result() --[[@as string[] ]]), opts.dump[2], code)
303386
if opts.callback then
304387
return opts.callback(output)
305388
else
@@ -320,8 +403,21 @@ end
320403

321404
-- Main ----------------------------------------------------
322405
------------------------------------------------------------
406+
407+
---@class PlenaryCurl
408+
---@field get PlenaryCurlMethod
409+
---@field post PlenaryCurlMethod
410+
---@field put PlenaryCurlMethod
411+
---@field head PlenaryCurlMethod
412+
---@field patch PlenaryCurlMethod
413+
---@field delete PlenaryCurlMethod
414+
---@field request PlenaryCurlMethod
415+
416+
---@return PlenaryCurl
323417
return (function()
324418
local spec = {}
419+
---@param method "get"|"post"|"put"|"head"|"patch"|"delete"|"request"
420+
---@return PlenaryCurlMethod
325421
local partial = function(method)
326422
return function(url, opts)
327423
opts = opts or {}

lua/plenary/init.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
---@class Plenary
33
---@field async PlenaryAsync
44
---@field context_manager PlenaryContextManager
5+
---@field curl PlenaryCurl
56
---@field functional PlenaryFunctional
67
---@field job PlenaryJob
78
---@field path PlenaryPath

tests/plenary/curl_spec.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ describe("CURL Wrapper:", function()
8484
return done
8585
end)
8686

87-
eq(403, res.status, "It should return 403")
87+
eq(403, res and res.status, "It should return 403")
8888
assert(not succ, "It should fail")
8989

9090
vim.fn.delete(loc)

0 commit comments

Comments
 (0)