PageRenderTime 32ms CodeModel.GetById 10ms app.highlight 15ms RepoModel.GetById 1ms app.codeStats 0ms

/src/luarocks/persist.lua

http://github.com/keplerproject/luarocks
Lua | 257 lines | 185 code | 21 blank | 51 comment | 30 complexity | f505a66b2dcce2267f65c66e165354a2 MD5 | raw file
  1
  2--- Utility module for loading files into tables and
  3-- saving tables into files.
  4local persist = {}
  5
  6local core = require("luarocks.core.persist")
  7local util = require("luarocks.util")
  8local dir = require("luarocks.dir")
  9local fs = require("luarocks.fs")
 10
 11persist.run_file = core.run_file
 12persist.load_into_table = core.load_into_table
 13
 14local write_table
 15
 16--- Write a value as Lua code.
 17-- This function handles only numbers and strings, invoking write_table
 18-- to write tables.
 19-- @param out table or userdata: a writer object supporting :write() method.
 20-- @param v: the value to be written.
 21-- @param level number: the indentation level
 22-- @param sub_order table: optional prioritization table
 23-- @see write_table
 24function persist.write_value(out, v, level, sub_order)
 25   if type(v) == "table" then
 26      level = level or 0
 27      write_table(out, v, level + 1, sub_order)
 28   elseif type(v) == "string" then
 29      if v:match("[\r\n]") then
 30         local open, close = "[[", "]]"
 31         local equals = 0
 32         local v_with_bracket = v.."]"
 33         while v_with_bracket:find(close, 1, true) do
 34            equals = equals + 1
 35            local eqs = ("="):rep(equals)
 36            open, close = "["..eqs.."[", "]"..eqs.."]"
 37         end
 38         out:write(open.."\n"..v..close)
 39      else
 40         out:write(("%q"):format(v))
 41      end
 42   else
 43      out:write(tostring(v))
 44   end
 45end
 46
 47local is_valid_plain_key
 48do
 49   local keywords = {
 50      ["and"] = true,
 51      ["break"] = true,
 52      ["do"] = true,
 53      ["else"] = true,
 54      ["elseif"] = true,
 55      ["end"] = true,
 56      ["false"] = true,
 57      ["for"] = true,
 58      ["function"] = true,
 59      ["goto"] = true,
 60      ["if"] = true,
 61      ["in"] = true,
 62      ["local"] = true,
 63      ["nil"] = true,
 64      ["not"] = true,
 65      ["or"] = true,
 66      ["repeat"] = true,
 67      ["return"] = true,
 68      ["then"] = true,
 69      ["true"] = true,
 70      ["until"] = true,
 71      ["while"] = true,
 72   }
 73   function is_valid_plain_key(k)
 74      return type(k) == "string"
 75             and k:match("^[a-zA-Z_][a-zA-Z0-9_]*$")
 76             and not keywords[k]
 77   end
 78end
 79
 80local function write_table_key_assignment(out, k, level)
 81   if is_valid_plain_key(k) then
 82      out:write(k)
 83   else
 84      out:write("[")
 85      persist.write_value(out, k, level)
 86      out:write("]")
 87   end
 88   
 89   out:write(" = ")
 90end
 91
 92--- Write a table as Lua code in curly brackets notation to a writer object.
 93-- Only numbers, strings and tables (containing numbers, strings
 94-- or other recursively processed tables) are supported.
 95-- @param out table or userdata: a writer object supporting :write() method.
 96-- @param tbl table: the table to be written.
 97-- @param level number: the indentation level
 98-- @param field_order table: optional prioritization table
 99write_table = function(out, tbl, level, field_order)
100   out:write("{")
101   local sep = "\n"
102   local indentation = "   "
103   local indent = true
104   local i = 1
105   for k, v, sub_order in util.sortedpairs(tbl, field_order) do
106      out:write(sep)
107      if indent then
108         for _ = 1, level do out:write(indentation) end
109      end
110
111      if k == i then
112         i = i + 1
113      else
114         write_table_key_assignment(out, k, level)
115      end
116
117      persist.write_value(out, v, level, sub_order)
118      if type(v) == "number" then
119         sep = ", "
120         indent = false
121      else
122         sep = ",\n"
123         indent = true
124      end
125   end
126   if sep ~= "\n" then
127      out:write("\n")
128      for _ = 1, level - 1 do out:write(indentation) end
129   end
130   out:write("}")
131end
132
133--- Write a table as series of assignments to a writer object.
134-- @param out table or userdata: a writer object supporting :write() method.
135-- @param tbl table: the table to be written.
136-- @param field_order table: optional prioritization table
137-- @return true if successful; nil and error message if failed.
138local function write_table_as_assignments(out, tbl, field_order)
139   for k, v, sub_order in util.sortedpairs(tbl, field_order) do
140      if not is_valid_plain_key(k) then
141         return nil, "cannot store '"..tostring(k).."' as a plain key."
142      end
143      out:write(k.." = ")
144      persist.write_value(out, v, 0, sub_order)
145      out:write("\n")
146   end
147   return true
148end
149
150--- Write a table using Lua table syntax to a writer object.
151-- @param out table or userdata: a writer object supporting :write() method.
152-- @param tbl table: the table to be written.
153local function write_table_as_table(out, tbl)
154   out:write("return {\n")
155   for k, v, sub_order in util.sortedpairs(tbl) do
156      out:write("   ")
157      write_table_key_assignment(out, k, 1)
158      persist.write_value(out, v, 1, sub_order)
159      out:write(",\n")
160   end
161   out:write("}\n")
162end
163
164--- Save the contents of a table to a string.
165-- Each element of the table is saved as a global assignment.
166-- Only numbers, strings and tables (containing numbers, strings
167-- or other recursively processed tables) are supported.
168-- @param tbl table: the table containing the data to be written
169-- @param field_order table: an optional array indicating the order of top-level fields.
170-- @return persisted data as string; or nil and an error message
171function persist.save_from_table_to_string(tbl, field_order)
172   local out = {buffer = {}}
173   function out:write(data) table.insert(self.buffer, data) end
174   local ok, err = write_table_as_assignments(out, tbl, field_order)
175   if not ok then
176      return nil, err
177   end
178   return table.concat(out.buffer)
179end
180
181--- Save the contents of a table in a file.
182-- Each element of the table is saved as a global assignment.
183-- Only numbers, strings and tables (containing numbers, strings
184-- or other recursively processed tables) are supported.
185-- @param filename string: the output filename
186-- @param tbl table: the table containing the data to be written
187-- @param field_order table: an optional array indicating the order of top-level fields.
188-- @return boolean or (nil, string): true if successful, or nil and a
189-- message in case of errors.
190function persist.save_from_table(filename, tbl, field_order)
191   local out = io.open(filename, "w")
192   if not out then
193      return nil, "Cannot create file at "..filename
194   end
195   local ok, err = write_table_as_assignments(out, tbl, field_order)
196   out:close()
197   if not ok then
198      return nil, err
199   end
200   return true
201end
202
203--- Save the contents of a table as a module.
204-- The module contains a 'return' statement that returns the table.
205-- Only numbers, strings and tables (containing numbers, strings
206-- or other recursively processed tables) are supported.
207-- @param filename string: the output filename
208-- @param tbl table: the table containing the data to be written
209-- @return boolean or (nil, string): true if successful, or nil and a
210-- message in case of errors.
211function persist.save_as_module(filename, tbl)
212   local out = io.open(filename, "w")
213   if not out then
214      return nil, "Cannot create file at "..filename
215   end
216   write_table_as_table(out, tbl)
217   out:close()
218   return true
219end
220
221function persist.load_config_file_if_basic(filename, cfg)
222   local env = {
223      home = cfg.home
224   }
225   local result, err, errcode = persist.load_into_table(filename, env)
226   if errcode == "load" or errcode == "run" then
227      -- bad config file or depends on env, so error out
228      return nil, "Could not read existing config file " .. filename
229   end
230
231   local tbl
232   if errcode == "open" then
233      -- could not open, maybe file does not exist
234      tbl = {}
235   else
236      tbl = result
237      tbl.home = nil
238   end
239
240   return tbl
241end
242
243function persist.save_default_lua_version(prefix, lua_version)
244   local ok, err = fs.make_dir(prefix)
245   if not ok then
246      return nil, err
247   end
248   local fd, err = io.open(dir.path(prefix, "default-lua-version.lua"), "w")
249   if not fd then
250      return nil, err
251   end
252   fd:write('return "' .. lua_version .. '"\n')
253   fd:close()
254   return true
255end
256
257return persist