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 258