Luigit
repositories / dotfiles

dotfiles

bugabingas dorkfiles

owned by admin

neovim/lua/bugabinga/pivi/jsonl.lua

Raw
local LF = '\n'

local strip_carriage_return = function( record )
  if record:sub( -1 ) == '\r' then return record:sub( 1, -2 ) end
  return record
end

--- Creates a strict JSONL record splitter.
--- Records are delimited by LF only. A trailing CR is stripped.
--- Unicode line separators inside JSON strings never split a record.
local new_splitter = function()
  local pending = ''

  --- @param chunk string? bytes received from the child process
  --- @return string[] records complete records contained in the chunk
  local feed = function( chunk )
    local records = {}
    if chunk == nil or chunk == '' then return records end

    pending = pending .. chunk

    while true do
      local index = pending:find( LF, 1, true )
      if not index then break end
      local record = strip_carriage_return( pending:sub( 1, index - 1 ) )
      pending = pending:sub( index + 1 )
      if record ~= '' then table.insert( records, record ) end
    end

    return records
  end

  --- @return string[] records the unterminated remainder, if any
  local finish = function()
    local remainder = strip_carriage_return( pending )
    pending = ''
    if remainder == '' then return {} end
    return { remainder, }
  end

  local buffered = function() return pending end

  return {
    feed = feed,
    finish = finish,
    buffered = buffered,
  }
end

--- @param record string one complete JSONL record
--- @return table? value decoded object, or nil when the record is not a JSON object
--- @return string? error decode failure description
local parse = function( record )
  local ok, value = pcall( vim.json.decode, record, { luanil = { object = true, array = true, }, } )
  if not ok then return nil, tostring( value ) end
  if type( value ) ~= 'table' then return nil, 'record is not a JSON object' end
  return value, nil
end

return {
  splitter = new_splitter,
  parse = parse,
}