/src/luarocks/persist.lua

http://github.com/keplerproject/luarocks · Lua · 257 lines · 242 code · 5 blank · 10 comment · 6 complexity · f505a66b2dcce2267f65c66e165354a2 MD5 · raw file

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