Files
jasper.nvim/lua/jasper/api.lua

170 lines
4.5 KiB
Lua

--- jasper/api.lua
--- Low-level HTTP calls to the Jasper API, executed via curl.
local M = {}
local JASPER_URL = "https://jasper.4sigma.it"
--- Run a curl command and return the parsed JSON body + HTTP status code.
--- @param args string[] full curl argument list (without the URL, which is last)
--- @return table|nil decoded JSON body
--- @return number HTTP status code (0 on curl error)
--- @return string|nil error message
local function curl(args)
-- Append -w to get the HTTP status on a separate last line
table.insert(args, "-w")
table.insert(args, "\n%{http_code}")
local result = vim.system(args, { text = true }):wait()
if result.code ~= 0 then
return nil, 0, "curl error (exit " .. result.code .. ")"
end
-- Split body and status code
local last_newline = result.stdout:match(".*\n()")
local status_code = tonumber(result.stdout:sub(last_newline)) or 0
local body = result.stdout:sub(1, last_newline - 2) -- strip trailing \n + status line
local ok, data = pcall(vim.fn.json_decode, body)
if not ok then
return nil, status_code, "JSON decode error"
end
return data, status_code, nil
end
--- @param path string API path, e.g. "/api/v1/timer/"
--- @param token string
--- @return table|nil, number, string|nil
local function get(path, token)
return curl({
"curl",
"-H",
"Authorization: Token " .. token,
JASPER_URL .. path,
})
end
--- @param path string
--- @param token string
--- @param form table<string,any>|nil key/value pairs sent as form data
--- @return table|nil, number, string|nil
local function post(path, token, form)
local args = {
"curl",
"-X",
"POST",
"-H",
"Authorization: Token " .. token,
}
if form then
for k, v in pairs(form) do
table.insert(args, "--data-urlencode")
table.insert(args, k .. "=" .. tostring(v))
end
end
table.insert(args, JASPER_URL .. path)
return curl(args)
end
-- ---------------------------------------------------------------------------
-- Public API
-- ---------------------------------------------------------------------------
--- Fetch all timers for the current user.
--- @param token string
--- @return table[]|nil list of timer objects
--- @return string|nil error message
function M.get_timers(token)
local data, status, err = get("/api/v1/timer/", token)
if err then
return nil, err
end
if status == 401 then
return nil, "unauthorized"
end
if status ~= 200 then
return nil, "unexpected HTTP status " .. status
end
if not data then
return nil, "empty response"
end
local timers_dict = data.data and data.data.timers
if type(timers_dict) ~= "table" then
return nil, "unexpected response shape"
end
local timers = {}
for _, v in pairs(timers_dict) do
table.insert(timers, v)
end
return timers, nil
end
--- Start (play) a timer.
--- @param token string
--- @param timer_id number
--- @return boolean ok
--- @return string|nil error
function M.start_timer(token, timer_id)
local data, status, err = post("/api/v1/timer/" .. timer_id .. "/play/", token)
if err then
return false, err
end
if status ~= 200 then
return false, "HTTP " .. status
end
if not data then
return true, nil
end
if data.error ~= nil and data.error ~= vim.NIL then
return false, tostring(data.error)
end
return true, nil
end
--- Pause a timer.
--- @param token string
--- @param timer_id number
--- @return boolean ok
--- @return string|nil error
function M.stop_timer(token, timer_id)
local data, status, err = post("/api/v1/timer/" .. timer_id .. "/pausa/", token)
if err then
return false, err
end
if status ~= 200 then
return false, "HTTP " .. status
end
if not data then
return true, nil
end
if data.error ~= nil and data.error ~= vim.NIL then
return false, tostring(data.error)
end
return true, nil
end
--- Associate an attivita with a (new) timer slot.
--- @param token string
--- @param timer_id number desired slot id (smallest free integer)
--- @param attivita_id number
--- @return boolean ok
--- @return string|nil error
function M.create_timer(token, timer_id, attivita_id)
local data, status, err = post("/api/v1/timer/" .. timer_id .. "/attivita/", token, { attivita_id = attivita_id })
if err then
return false, err
end
if status ~= 200 then
return false, "HTTP " .. status
end
if not data then
return true, nil
end
if data.error ~= nil and data.error ~= vim.NIL then
return false, tostring(data.error)
end
return true, nil
end
return M