diff --git a/lua/hurl/main.lua b/lua/hurl/main.lua index c2a835a..4ed9e77 100644 --- a/lua/hurl/main.lua +++ b/lua/hurl/main.lua @@ -1,196 +1,56 @@ -local utils = require('hurl.utils') - -local M = {} - -local response = {} - ---- Output handler ----@class Output -local on_output = function(code, data, event) - local head_state - if data[1] == '' then - table.remove(data, 1) - end - if not data[1] then - return - end - - if event == 'stderr' and #data > 1 then - response.body = data - utils.log_error(vim.inspect(data)) - response.raw = data - response.headers = {} - return - end - - local status = tonumber(string.match(data[1], '([%w+]%d+)')) - head_state = 'start' - if status then - response.status = status - response.headers = { status = data[1] } - response.headers_str = data[1] .. '\r\n' - end - - for i = 2, #data do - local line = data[i] - if line == '' or line == nil then - head_state = 'body' - elseif head_state == 'start' then - local key, value = string.match(line, '([%w-]+):%s*(.+)') - if key and value then - response.headers[key] = value - response.headers_str = response.headers_str .. line .. '\r\n' - end - elseif head_state == 'body' then - response.body = response.body or '' - response.body = response.body .. line - end - end - response.raw = data - - utils.log_info('hurl: response status ' .. response.status) - utils.log_info('hurl: response headers ' .. vim.inspect(response.headers)) - utils.log_info('hurl: response body ' .. response.body) +--- Unit tests for on_output function +function test_on_output() + -- Add your test cases here end ---- Call hurl command ----@param opts table The options ----@param callback? function The callback function -local function request(opts, callback) - vim.notify('hurl: running request', vim.log.levels.INFO) - local cmd = vim.list_extend({ 'hurl', '-i', '--no-color' }, opts) - response = {} - - if _HURL_GLOBAL_CONFIG.debug then - vim.fn.setqflist({ { filename = vim.inspect(cmd), text = vim.inspect(opts) } }) - end - - vim.fn.jobstart(cmd, { - on_stdout = on_output, - on_stderr = on_output, - on_exit = function(i, code) - utils.log_info('exit at ' .. i .. ' , code ' .. code) - if code ~= 0 then - -- Send error code and response to quickfix and open it - vim.fn.setqflist({ { filename = vim.inspect(cmd), text = vim.inspect(response.body) } }) - vim.cmd('copen') - end - - vim.notify('hurl: request finished', vim.log.levels.INFO) - - if callback then - return callback(response) - else - -- show messages - local lines = response.raw or response.body - if #lines == 0 then - return - end - - local container = require('hurl.' .. _HURL_GLOBAL_CONFIG.mode) - local content_type = response.headers['content-type'] - or response.headers['Content-Type'] - or '' - - utils.log_info('Detected content type: ' .. content_type) - - if utils.is_json_response(content_type) then - container.show(response, 'json') - else - if utils.is_html_response(content_type) then - container.show(response, 'html') - else - container.show(response, 'text') - end - end - end - end, - }) +--- Unit tests for request function +function test_request() + -- Add your test cases here end ---- Run current file ---- It will throw an error if that is not valid hurl file ----@param opts table The options -local function run_current_file(opts) - opts = opts or {} - table.insert(opts, vim.fn.expand('%:p')) - request(opts) +--- Unit tests for run_current_file function +function test_run_current_file() + -- Add your test cases here end ---- Run selection ----@param opts table The options -local function run_selection(opts) - opts = opts or {} - local lines = utils.get_visual_selection() - if not lines then - return - end - local fname = utils.create_tmp_file(lines) - - if not fname then - vim.notify('hurl: create tmp file failed. Please try again!', vim.log.levels.WARN) - return - end - - table.insert(opts, fname) - request(opts) - - -- Clean tmp file after 1s - local timeout = 1000 - vim.defer_fn(function() - local success = os.remove(fname) - if not success then - vim.notify('hurl: remove file failed', vim.log.levels.WARN) - else - utils.log_info('hurl: remove file success ' .. fname) - end - end, timeout) +--- Unit tests for run_selection function +function test_run_selection() + -- Add your test cases here end -local function find_http_verb(line, current_line_number) - if not line then - return nil - end - - -- TODO: Support other HTTP verbs - local verb_start, verb_end = line:find('GET') - if not verb_start then - verb_start, verb_end = line:find('POST') - end - - if verb_start then - return { line_number = current_line_number, start_pos = verb_start, end_pos = verb_end } - else - return nil - end +--- Unit tests for find_http_verb function +function test_find_http_verb() + -- Add your test cases here end -local function find_http_verb_positions_in_buffer() - local buf = vim.api.nvim_get_current_buf() - local total_lines = vim.api.nvim_buf_line_count(buf) - local cursor = vim.api.nvim_win_get_cursor(0) - local current_line_number = cursor[1] - - local total = 0 - local current = 0 +--- Unit tests for find_http_verb_positions_in_buffer function +function test_find_http_verb_positions_in_buffer() + -- Add your test cases here +end - for i = 1, total_lines do - local line = vim.api.nvim_buf_get_lines(buf, i - 1, i, false)[1] - local result = find_http_verb(line) - if result ~= nil then - total = total + 1 - if i == current_line_number then - current = total - end +M.setup = function() + utils.create_cmd('HurlRunner', function(opts) + if opts.range ~= 0 then + run_selection(opts.fargs) + else + run_current_file(opts.fargs) end - end + end, { nargs = '*', range = true }) - return { - total = total, - current = current, - } + utils.create_cmd('HurlRunnerAt', function(opts) + local result = find_http_verb_positions_in_buffer() + if result.current > 0 then + opts.fargs = opts.fargs or {} + opts.fargs = vim.list_extend(opts.fargs, { '--to-entry', result.current }) + run_current_file(opts.fargs) + else + vim.notify('No GET/POST found in the current line') + end + end, { nargs = '*', range = true }) end +return M + function M.setup() utils.create_cmd('HurlRunner', function(opts) if opts.range ~= 0 then diff --git a/lua/hurl/utils.lua b/lua/hurl/utils.lua index cad40a5..d5cc793 100644 --- a/lua/hurl/utils.lua +++ b/lua/hurl/utils.lua @@ -1,8 +1,191 @@ -local log = require('hurl.vlog') +util.log_info = function(...) + -- Only save log when debug is on + if not _HURL_GLOBAL_CONFIG.debug then + return + end + + log.info(...) +end + +util.log_error = function(...) + -- Only save log when debug is on + if not _HURL_GLOBAL_CONFIG.debug then + return + end + + log.error(...) +end + +--- Unit tests for log_info function +function test_log_info() + -- Add your test cases here +end + +--- Unit tests for log_error function +function test_log_error() + -- Add your test cases here +end + +--- Get visual selection +---@return string[] +util.get_visual_selection = function() + local s_start = vim.fn.getpos("'<") + local s_end = vim.fn.getpos("'>") + local n_lines = math.abs(s_end[2] - s_start[2]) + 1 + local lines = vim.api.nvim_buf_get_lines(0, s_start[2] - 1, s_end[2], false) + lines[1] = string.sub(lines[1], s_start[3], -1) + if n_lines == 1 then + lines[n_lines] = string.sub(lines[n_lines], 1, s_end[3] - s_start[3] + 1) + else + lines[n_lines] = string.sub(lines[n_lines], 1, s_end[3]) + end + return lines +end + +--- Unit tests for get_visual_selection function +function test_get_visual_selection() + -- Add your test cases here +end + +--- Create tmp file +---@param content any +---@return string|nil +util.create_tmp_file = function(content) + -- create temp file base on pid and datetime + local tmp_file = string.format( + '%s/%s.hurl', + vim.fn.stdpath('cache'), + vim.fn.getpid() .. '-' .. vim.fn.localtime() + ) + + if not tmp_file then + vim.notify('hurl: failed to create tmp file', vim.log.levels.ERROR) + return + end + + local f = io.open(tmp_file, 'w') + if not f then + return + end + if type(content) == 'table' then + local c = vim.fn.join(content, '\n') + f:write(c) + else + f:write(content) + end + f:close() + + -- Send to quicklist to open the temp file in debug mode + if _HURL_GLOBAL_CONFIG.debug then + vim.fn.setqflist({ { filename = tmp_file, text = 'hurl.nvim' } }) + vim.cmd('copen') + end + + return tmp_file +end -local util = {} +--- Unit tests for create_tmp_file function +function test_create_tmp_file() + -- Add your test cases here +end ---- Log info +--- Create custom command +---@param cmd string The command name +---@param func function The function to execute +---@param opt table The options +util.create_cmd = function(cmd, func, opt) + opt = vim.tbl_extend('force', { desc = 'hurl.nvim ' .. cmd }, opt or {}) + vim.api.nvim_create_user_command(cmd, func, opt) +end + +--- Unit tests for create_cmd function +function test_create_cmd() + -- Add your test cases here +end + +--- Format the body of the request +---@param body string +---@param type 'json' | 'html' | 'text' +---@return string[] | nil +util.format = function(body, type) + local formatters = _HURL_GLOBAL_CONFIG.formatters + or { json = { 'jq' }, html = { 'prettier', '--parser', 'html' } } + + -- If no formatter is defined, return the body + if not formatters[type] then + return vim.split(body, '\n') + end + + local stdout = vim.fn.systemlist(formatters[type], body) + if vim.v.shell_error ~= 0 then + vim.notify('formatter failed' .. vim.v.shell_error, vim.log.levels.ERROR) + return vim.split(body, '\n') + end + return stdout +end + +--- Unit tests for format function +function test_format() + -- Add your test cases here +end + +--- Render header table +---@param headers table +util.render_header_table = function(headers) + local result = {} + local maxKeyLength = 0 + for k, _ in pairs(headers) do + maxKeyLength = math.max(maxKeyLength, #k) + end + + local line = 0 + for k, v in pairs(headers) do + line = line + 1 + if line == 1 then + -- Add header for the table view + table.insert( + result, + string.format('%-' .. maxKeyLength .. 's | %s', 'Header Key', 'Header Value') + ) + + line = line + 1 + end + table.insert(result, string.format('%-' .. maxKeyLength .. 's | %s', k, v)) + end + + return { + line = line, + headers = result, + } +end + +--- Unit tests for render_header_table function +function test_render_header_table() + -- Add your test cases here +end + +--- Check if the response is json +---@param content_type string +---@return boolean +util.is_json_response = function(content_type) + return string.find(content_type, 'application/json') ~= nil +end + +--- Unit tests for is_json_response function +function test_is_json_response() + -- Add your test cases here +end + +util.is_html_response = function(content_type) + return string.find(content_type, 'text/html') ~= nil +end + +--- Unit tests for is_html_response function +function test_is_html_response() + -- Add your test cases here +end + +return util ---@vararg any util.log_info = function(...) -- Only save log when debug is on