Skip to content

Commit dd2e07d

Browse files
committed
feat: add type annotations for plenary.curl
1 parent e601d3d commit dd2e07d

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

@@ -41,6 +46,8 @@ local compat = require "plenary.compat"
4146
-- Utils ----------------------------------------------------
4247
-------------------------------------------------------------
4348

49+
---@param str string|integer
50+
---@return string|integer
4451
util.url_encode = function(str)
4552
if type(str) ~= "number" then
4653
str = str:gsub("\r?\n", "\r\n")
@@ -54,12 +61,20 @@ util.url_encode = function(str)
5461
end
5562
end
5663

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

74+
---@param kv table<string, string>
75+
---@param sep? string
76+
---@param kvsep string
77+
---@return string
6378
util.kv_to_str = function(kv, sep, kvsep)
6479
return F.join(
6580
F.kv_map(function(kvp)
@@ -69,6 +84,7 @@ util.kv_to_str = function(kv, sep, kvsep)
6984
)
7085
end
7186

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

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

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

141+
---@param xs? string|table<string, string>
142+
---@return string[]?
117143
parse.raw_body = function(xs)
118144
if not xs then
119145
return
@@ -125,20 +151,26 @@ parse.raw_body = function(xs)
125151
end
126152
end
127153

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

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

172+
---@param s? string
173+
---@return { [1]: "-I"|"-X", [2]: string? }?
142174
parse.method = function(s)
143175
if not s then
144176
return
@@ -150,39 +182,50 @@ parse.method = function(s)
150182
end
151183
end
152184

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

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

203+
---@param xs string
204+
---@param q table<string, string>
205+
---@return string?
167206
parse.url = function(xs, q)
168207
if not xs then
169208
return
170209
end
171-
q = parse.curl_query(q)
210+
local query = parse.curl_query(q)
172211
if type(xs) == "string" then
173-
return q and xs .. "?" .. q or xs
212+
return query and xs .. "?" .. query or xs
174213
elseif type(xs) == "table" then
175214
error "Low level URL definition is not supported."
176215
end
177216
end
178217

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

227+
---@param s string?
228+
---@return { [1]: string }?
186229
parse.http_version = function(s)
187230
if not s then
188231
return
@@ -198,9 +241,36 @@ end
198241

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

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

348+
---@param specs PlenaryCurlOptions
349+
---@return PlenaryCurlResponse|PlenaryJob|string[]
268350
local request = function(specs)
269351
local response = {}
270352
local args, opts = parse.request(vim.tbl_extend("force", {
@@ -277,6 +359,7 @@ local request = function(specs)
277359
return args
278360
end
279361

362+
---@type PlenaryJobOptions
280363
local job_opts = {
281364
command = vim.g.plenary_curl_bin_path or "curl",
282365
args = args,
@@ -300,7 +383,7 @@ local request = function(specs)
300383
error(message)
301384
end
302385
end
303-
local output = parse.response(j:result(), opts.dump[2], code)
386+
local output = parse.response((j:result() --[[@as string[] ]]), opts.dump[2], code)
304387
if opts.callback then
305388
return opts.callback(output)
306389
else
@@ -321,8 +404,21 @@ end
321404

322405
-- Main ----------------------------------------------------
323406
------------------------------------------------------------
407+
408+
---@class PlenaryCurl
409+
---@field get PlenaryCurlMethod
410+
---@field post PlenaryCurlMethod
411+
---@field put PlenaryCurlMethod
412+
---@field head PlenaryCurlMethod
413+
---@field patch PlenaryCurlMethod
414+
---@field delete PlenaryCurlMethod
415+
---@field request PlenaryCurlMethod
416+
417+
---@return PlenaryCurl
324418
return (function()
325419
local spec = {}
420+
---@param method "get"|"post"|"put"|"head"|"patch"|"delete"|"request"
421+
---@return PlenaryCurlMethod
326422
local partial = function(method)
327423
return function(url, opts)
328424
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)