
--- Utility module for loading files into tables and
-- saving tables into files.
-- Implemented separately to avoid interdependencies,
-- as it is used in the bootstrapping stage of the cfg module.
local io = io
local print = print
module("luarocks.persist", package.seeall)

local util = require("luarocks.util")

--- Load a Lua file containing assignments, storing them in a table.
-- The global environment is not propagated to the loaded file.
-- @param filename string: the name of the file.
-- @param tbl table or nil: if given, this table is used to store
-- loaded values.
-- @return table or (nil, string): a table with the file's assignments
-- as fields, or nil and a message in case of errors.
function load_into_table(filename, tbl)
   assert(type(filename) == "string")
   assert(type(tbl) == "table" or not tbl)

   local chunk, err = loadfile(filename)
   if not chunk then
      return nil, err
   end
   local result = tbl or {}
   setfenv(chunk, result)
   chunk()
   return result
end

--- Write a table as Lua code representing a table to disk
-- (that is, in curly brackets notation).
-- This function handles only numbers, strings and tables
-- are keys (tables are handled recursively).
-- @param out userdata: a file object, open for writing.
-- @param tbl table: the table to be written.
local function write_table(out, tbl, level)
   out:write("{")
   local sep = "\n"
   local indent = true
   local i = 1
   for k, v in util.sortedpairs(tbl) do
      out:write(sep)
      if indent then
         for n = 1,level do out:write("  ") end
      end
      sep = ",\n"
      indent = true
      if type(k) == "number" then
         if k ~= i then
            out:write('['..tostring(k).."]=")
         else
            i = i + 1
         end
         indent = false
         sep = ", "
      elseif type(k) == "table" then
         out:write("[")
         write_table(out, k, level + 1)
         out:write("]=")
      else
         if k:match("^[a-z_]+$") then
            out:write(k.."=")
         else
            out:write("['"..k:gsub("'", "\\'").."']=") 
         end
      end
      if type(v) == "table" then
         write_table(out, v, level + 1)
      elseif type(v) == "string" then
         out:write("'"..v:gsub("'", "\\'").."'")
      else
         out:write(tostring(v))
      end
   end
   if sep ~= "\n" then
      out:write("\n")
      for n = 1,level-1 do out:write("  ") end
   end
   out:write("}")
end

--- Save the contents of a table in a file.
-- Each element of the table is saved as a global assignment.
-- Only numbers, strings and tables (containing numbers, strings
-- or other recursively processed tables) are supported.
-- @return boolean or (nil, string): true if successful, or nil and a
-- message in case of errors.
function save_from_table(filename, tbl)
   local out = io.open(filename, "w")
   if not out then
      return nil, "Cannot create file at "..filename
   end
   for k, v in util.sortedpairs(tbl) do
      out:write(k.." = ")
      write_table(out, v, 1)
      out:write("\n")
   end
   out:close()
   return true
end
