@@ -72,52 +72,99 @@ local function parse_headers(req_node, source, context)
72
72
return setmetatable (headers , nil )
73
73
end
74
74
75
+ --- @param str string
76
+ --- @return boolean
77
+ local function validate_json (str )
78
+ local ok , _ = pcall (vim .json .decode , str )
79
+ return ok
80
+ end
81
+
82
+ --- @param str string
83
+ --- @return boolean
84
+ local function validate_xml (str )
85
+ local xml2lua = require (" xml2lua" )
86
+ local handler = require (" xmlhandler.tree" ):new ()
87
+ local xml_parser = xml2lua .parser (handler )
88
+ local ok = pcall (function (t ) return xml_parser :parse (t ) end , str )
89
+ return ok
90
+ end
91
+
92
+ --- @param str string
93
+ --- @return table<string,string> ?
94
+ local function parse_urlencoded_form (str )
95
+ local form = {}
96
+ local query_pairs = vim .split (str , " &" )
97
+ for _ , query in ipairs (query_pairs ) do
98
+ local key , value = query :match (" ([^=]+)=?(.*)" )
99
+ if not key then
100
+ -- TODO: error
101
+ return nil
102
+ end
103
+ form [vim .trim (key )] = vim .trim (value )
104
+ end
105
+ return form
106
+ end
107
+
108
+ --- @param content_type string ?
75
109
--- @param body_node TSNode
76
110
--- @param source Source
77
111
--- @param context rest.Context
78
- --- @return rest.Request.Body | nil
79
- function parser .parse_body (body_node , source , context )
112
+ --- @return rest.Request.Body ?
113
+ function parser .parse_body (content_type , body_node , source , context )
80
114
local body = {}
81
- body . __TYPE = body_node :type (): gsub ( " _%w+ " , " " )
115
+ local node_type = body_node :type ()
82
116
--- @cast body rest.Request.Body
83
- if body .__TYPE == " json" then
117
+ if node_type == " external_body" then
118
+ body .__TYPE = " external"
119
+ local path = assert (get_node_field_text (body_node , " path" , source ))
120
+ if type (source ) ~= " number" then
121
+ logger .error (" can't parse external body on non-existing http file" )
122
+ return
123
+ end
124
+ --- @cast source integer
125
+ local basepath = vim .api .nvim_buf_get_name (source ):match (" (.*)/.*" )
126
+ path = vim .fs .normalize (vim .fs .joinpath (basepath , path ))
127
+ body .data = {
128
+ name = get_node_field_text (body_node , " name" , source ),
129
+ path = path ,
130
+ }
131
+ elseif node_type == " json_body" or content_type == " application/json" then
132
+ body .__TYPE = " json"
84
133
body .data = vim .trim (vim .treesitter .get_node_text (body_node , source ))
85
134
body .data = expand_variables (body .data , context )
86
- local ok , _ = pcall ( vim . json . decode , body .data )
135
+ local ok = validate_json ( body .data )
87
136
if not ok then
88
137
logger .warn (" invalid json: '" .. body .data .. " '" )
89
138
return nil
90
139
end
91
- elseif body .__TYPE == " xml" then
140
+ elseif node_type == " xml_body" or content_type == " application/xml" then
141
+ body .__TYPE = " xml"
92
142
body .data = vim .trim (vim .treesitter .get_node_text (body_node , source ))
93
143
body .data = expand_variables (body .data , context )
94
- local xml2lua = require (" xml2lua" )
95
- local handler = require (" xmlhandler.tree" ):new ()
96
- local xml_parser = xml2lua .parser (handler )
97
- local ok = pcall (function (t ) return xml_parser :parse (t ) end , body .data )
144
+ local ok = validate_xml (body .data )
98
145
if not ok then
99
146
logger .warn (" invalid xml: '" .. body .data .. " '" )
100
147
return nil
101
148
end
102
- elseif body . __TYPE == " form " then
103
- body . data = {}
104
- for pair , _ in body_node : iter_children () do
105
- if pair : type () == " query " then
106
- local key = assert ( get_node_field_text ( pair , " key " , source ))
107
- local value = assert ( get_node_field_text ( pair , " value " , source ) )
108
- key = expand_variables ( key , context )
109
- value = expand_variables ( value , context )
110
- body . data [ key ] = value
149
+ elseif node_type == " raw_body " then
150
+ -- TODO: exclude comments from text
151
+ local text = vim . treesitter . get_node_text ( body_node , source )
152
+ if content_type and vim . startswith ( content_type , " application/x-www-form-urlencoded " ) then
153
+ body . __TYPE = " form "
154
+ body . data = parse_urlencoded_form ( text )
155
+ if not body . data then
156
+ -- TODO: parsing urlencoded form failed
157
+ return nil
111
158
end
159
+ else
160
+ body .__TYPE = " raw"
161
+ body .data = text
112
162
end
113
- elseif body .__TYPE == " external" then
114
- local path = assert (get_node_field_text (body_node , " path" , source ))
115
- path = vim .fs .normalize (vim .fs .joinpath (vim .fn .expand (" %:h" ), path ))
116
- body .data = {
117
- name = get_node_field_text (body_node , " name" , source ),
118
- path = path ,
119
- }
120
- elseif body .__TYPE == " graphql" then
163
+ elseif node_type == " multipart_form_data" then
164
+ body .__TYPE = " multipart_form_data"
165
+ -- TODO:
166
+ logger .error (" multipart form data is not supported yet" )
167
+ elseif node_type == " graphql_body" then
121
168
logger .error (" graphql body is not supported yet" )
122
169
end
123
170
return body
@@ -273,15 +320,6 @@ function parser.parse(node, source, ctx)
273
320
logger .error (" request section doesn't have request node" )
274
321
return nil
275
322
end
276
- local body
277
- local body_node = req_node :field (" body" )[1 ]
278
- if body_node then
279
- body = parser .parse_body (body_node , source , ctx )
280
- if not body then
281
- logger .error (" parsing body failed" )
282
- return nil
283
- end
284
- end
285
323
local method = get_node_field_text (req_node , " method" , source )
286
324
if not method then
287
325
logger .info (" no method provided, falling back to 'GET'" )
@@ -334,6 +372,23 @@ function parser.parse(node, source, ctx)
334
372
url = host .. url
335
373
table.remove (headers [" host" ], 1 )
336
374
end
375
+
376
+ --- @type string ?
377
+ local content_type
378
+ if headers [" content-type" ] then
379
+ content_type = headers [" content-type" ][1 ]:match (" ([^;]+)" )
380
+ end
381
+ local body
382
+ local body_node = req_node :field (" body" )[1 ]
383
+ if body_node then
384
+ body = parser .parse_body (content_type , body_node , source , ctx )
385
+ if not body then
386
+ logger .error (" parsing body failed" )
387
+ vim .notify (" [rest.nvim] parsing request body failed. See `:Rest logs` for more info." , vim .log .levels .ERROR )
388
+ return nil
389
+ end
390
+ end
391
+
337
392
--- @type rest.Request
338
393
local req = {
339
394
name = name ,
0 commit comments