You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

228 lines
7.5 KiB

local action_state = require('telescope.actions.state')
local actions = require('telescope.actions')
local conf = require("telescope.config").values
local finders = require("telescope.finders")
local pickers = require("telescope.pickers")
local previewers = require("telescope.previewers")
local log = require('plenary.log').new {
plugin = 'knot',
level = 'info',
}
local _get_name = function(record)
local name = record.name .. " (" .. record.rtype .. "): " .. record.data
return name
end
local _get_records_and_entries = function()
local results = vim.json.decode(vim.fn.system("knotctl --json list"))
local records = {}
for _, zone in ipairs(results) do
for _, record in ipairs(zone.records) do
if record then
record.zone = zone.name
table.insert(records, record)
end
end
end
local entries = vim.tbl_map(
function(record)
return _get_name(record)
end,
records
)
return records, entries
end
local _filter_records = function(rec, records)
local r = {}
local record_name = rec.value
for _, record in ipairs(records) do
local name = _get_name(record)
if name == record_name then
r = { "ZONE: " .. record.zone, "RTYPE: " .. record.rtype, "NAME: " .. record.name, "DATA: " .. record.data, "TTL: " ..
record.ttl }
end
end
return r
end
local _parse_record = function(rec)
local record = {}
for _, v in ipairs(rec) do
if v ~= nil and v ~= "" then
local key = v:match("%w+"):lower()
if vim.tbl_contains({ "zone", "rtype", "name", "data", "ttl" }, key) then
record[key] = v:gsub("%w+%:%s", "")
end
end
end
return record
end
local _validate_record = function(record)
if record.zone == nil or record.rtype == nil or record.name == nil or record.data == nil then
return false
end
return true
end
local M = {}
M.add = function(_)
local tempfile = vim.fn.tempname()
local buffer = vim.api.nvim_create_buf(false, false)
vim.api.nvim_buf_set_name(buffer, tempfile)
local r = { "ZONE: example.net.", "RTYPE: A", "NAME: example.net.", "DATA: 127.0.0.1", "TTL: 360" }
vim.api.nvim_buf_set_lines(buffer, 0, 0, false, r)
local width = vim.api.nvim_win_get_width(0)
local height = vim.api.nvim_win_get_height(0) - 10
vim.api.nvim_open_win(buffer, true,
{ relative = 'win', bufpos = { 0, 0 }, width = width, height = height, border = 'rounded' })
vim.api.nvim_create_autocmd({ "BufWipeout" }, {
buffer = buffer,
callback = function(_)
vim.api.nvim_buf_delete(buffer, { force = true })
vim.fn.delete(tempfile)
end
})
vim.api.nvim_create_autocmd({ "BufWrite" }, {
buffer = buffer,
callback = function(_)
local new = vim.api.nvim_buf_get_lines(buffer, 0, -1, false)
local record = _parse_record(new)
if _validate_record(record) then
local command = "knotctl add -z " .. record.zone ..
" -t " .. record.ttl ..
" -n " .. record.name ..
" -r " .. record.rtype ..
" -d " .. record.data
local choice = vim.fn.confirm("Create record?", "&Yes\n&No", 2)
if choice == 1 then
log.info(vim.fn.system(command))
end
else
log.error("Invalid record")
end
end
})
end
M.delete = function(opts)
local records, entries = _get_records_and_entries()
pickers.new(opts, {
dropdown = true,
prompt_title = "Records",
sorter = conf.generic_sorter(opts),
finder = finders.new_table(entries),
previewer = previewers.new_buffer_previewer({
title = "Record",
define_preview = function(self, entry)
local record = _filter_records(entry, records)
vim.api.nvim_buf_set_lines(self.state.bufnr, 0, 0, false, record)
end
}),
attach_mappings = function(prompt_bufnr)
actions.select_default:replace(function()
local entry = action_state.get_selected_entry()
local record = _parse_record(_filter_records(entry, records))
local choice = vim.fn.confirm("Delete record?", "&Yes\n&No", 2)
if choice == 1 then
local command = "knotctl delete -z " .. record.zone ..
" -n " .. record.name ..
" -r " .. record.rtype ..
" -d " .. record.data
log.info(vim.fn.system(command))
actions.close(prompt_bufnr)
end
end)
return true
end,
}):find()
end
M.update = function(opts)
local records, entries = _get_records_and_entries()
pickers.new(opts, {
dropdown = true,
prompt_title = "Records",
sorter = conf.generic_sorter(opts),
finder = finders.new_table(entries),
previewer = previewers.new_buffer_previewer({
title = "Record",
define_preview = function(self, entry)
local record = _filter_records(entry, records)
vim.api.nvim_buf_set_lines(self.state.bufnr, 0, 0, false, record)
end
}),
attach_mappings = function(prompt_bufnr)
actions.select_default:replace(function()
local entry = action_state.get_selected_entry()
local record = _filter_records(entry, records)
actions.close(prompt_bufnr)
local tempfile = vim.fn.tempname()
local buffer = vim.api.nvim_create_buf(false, false)
vim.api.nvim_buf_set_name(buffer, tempfile)
vim.api.nvim_buf_set_lines(buffer, 0, 0, false, record)
local width = vim.api.nvim_win_get_width(0)
local height = vim.api.nvim_win_get_height(0) - 10
vim.api.nvim_open_win(buffer, true,
{ relative = 'win', bufpos = { 0, 0 }, width = width, height = height, border = 'rounded' })
vim.api.nvim_create_autocmd({ "BufWipeout" }, {
buffer = buffer,
callback = function(_)
vim.api.nvim_buf_delete(buffer, { force = true })
vim.fn.delete(tempfile)
end
})
vim.api.nvim_create_autocmd({ "BufWrite" }, {
buffer = buffer,
callback = function(_)
local new = vim.api.nvim_buf_get_lines(buffer, 0, -1, false)
if new ~= record then
local cleanold = _parse_record(record)
local updated = {}
local did_update = false
local cleannew = {}
for _, v in ipairs(new) do
if v ~= nil and v ~= "" then
local key = v:match("%w+"):lower()
cleannew[key] = v:gsub("%w+%:%s", "")
if cleanold[key] ~= cleannew[key] and (key ~= "url" or key ~= "zone") then
table.insert(updated, " -a " .. key .. "=" .. cleannew[key])
did_update = true
end
end
end
if did_update then
if _validate_record(cleannew) and _validate_record(cleanold) then
local command = "knotctl update -z " .. cleanold.zone ..
" -t " .. cleanold.ttl ..
" -n " .. cleanold.name ..
" -r " .. cleanold.rtype ..
" -d " .. cleanold.data ..
table.concat(updated, " ")
local choice = vim.fn.confirm("Update record?", "&Yes\n&No", 2)
if choice == 1 then
log.info(vim.fn.system(command))
end
else
log.error("Invalid record")
end
end
end
end
})
end)
return true
end,
}):find()
end
return require('telescope').register_extension({
exports = {
add = M.add,
delete = M.delete,
update = M.update,
},
})