Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
luarocks/src/luarocks/persist.lua
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
259 lines (238 sloc)
7.8 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- Utility module for loading files into tables and | |
| -- saving tables into files. | |
| local persist = {} | |
| local core = require("luarocks.core.persist") | |
| local util = require("luarocks.util") | |
| local dir = require("luarocks.dir") | |
| local fs = require("luarocks.fs") | |
| persist.run_file = core.run_file | |
| persist.load_into_table = core.load_into_table | |
| local write_table | |
| --- Write a value as Lua code. | |
| -- This function handles only numbers and strings, invoking write_table | |
| -- to write tables. | |
| -- @param out table or userdata: a writer object supporting :write() method. | |
| -- @param v: the value to be written. | |
| -- @param level number: the indentation level | |
| -- @param sub_order table: optional prioritization table | |
| -- @see write_table | |
| function persist.write_value(out, v, level, sub_order) | |
| if type(v) == "table" then | |
| level = level or 0 | |
| write_table(out, v, level + 1, sub_order) | |
| elseif type(v) == "string" then | |
| if v:match("[\r\n]") then | |
| local open, close = "[[", "]]" | |
| local equals = 0 | |
| local v_with_bracket = v.."]" | |
| while v_with_bracket:find(close, 1, true) do | |
| equals = equals + 1 | |
| local eqs = ("="):rep(equals) | |
| open, close = "["..eqs.."[", "]"..eqs.."]" | |
| end | |
| out:write(open.."\n"..v..close) | |
| else | |
| out:write(("%q"):format(v)) | |
| end | |
| else | |
| out:write(tostring(v)) | |
| end | |
| end | |
| local is_valid_plain_key | |
| do | |
| local keywords = { | |
| ["and"] = true, | |
| ["break"] = true, | |
| ["do"] = true, | |
| ["else"] = true, | |
| ["elseif"] = true, | |
| ["end"] = true, | |
| ["false"] = true, | |
| ["for"] = true, | |
| ["function"] = true, | |
| ["goto"] = true, | |
| ["if"] = true, | |
| ["in"] = true, | |
| ["local"] = true, | |
| ["nil"] = true, | |
| ["not"] = true, | |
| ["or"] = true, | |
| ["repeat"] = true, | |
| ["return"] = true, | |
| ["then"] = true, | |
| ["true"] = true, | |
| ["until"] = true, | |
| ["while"] = true, | |
| } | |
| function is_valid_plain_key(k) | |
| return type(k) == "string" | |
| and k:match("^[a-zA-Z_][a-zA-Z0-9_]*$") | |
| and not keywords[k] | |
| end | |
| end | |
| local function write_table_key_assignment(out, k, level) | |
| if is_valid_plain_key(k) then | |
| out:write(k) | |
| else | |
| out:write("[") | |
| persist.write_value(out, k, level) | |
| out:write("]") | |
| end | |
| out:write(" = ") | |
| end | |
| --- Write a table as Lua code in curly brackets notation to a writer object. | |
| -- Only numbers, strings and tables (containing numbers, strings | |
| -- or other recursively processed tables) are supported. | |
| -- @param out table or userdata: a writer object supporting :write() method. | |
| -- @param tbl table: the table to be written. | |
| -- @param level number: the indentation level | |
| -- @param field_order table: optional prioritization table | |
| write_table = function(out, tbl, level, field_order) | |
| out:write("{") | |
| local sep = "\n" | |
| local indentation = " " | |
| local indent = true | |
| local i = 1 | |
| for k, v, sub_order in util.sortedpairs(tbl, field_order) do | |
| out:write(sep) | |
| if indent then | |
| for _ = 1, level do out:write(indentation) end | |
| end | |
| if k == i then | |
| i = i + 1 | |
| else | |
| write_table_key_assignment(out, k, level) | |
| end | |
| persist.write_value(out, v, level, sub_order) | |
| if type(v) == "number" then | |
| sep = ", " | |
| indent = false | |
| else | |
| sep = ",\n" | |
| indent = true | |
| end | |
| end | |
| if sep ~= "\n" then | |
| out:write("\n") | |
| for _ = 1, level - 1 do out:write(indentation) end | |
| end | |
| out:write("}") | |
| end | |
| --- Write a table as series of assignments to a writer object. | |
| -- @param out table or userdata: a writer object supporting :write() method. | |
| -- @param tbl table: the table to be written. | |
| -- @param field_order table: optional prioritization table | |
| -- @return true if successful; nil and error message if failed. | |
| local function write_table_as_assignments(out, tbl, field_order) | |
| for k, v, sub_order in util.sortedpairs(tbl, field_order) do | |
| if not is_valid_plain_key(k) then | |
| return nil, "cannot store '"..tostring(k).."' as a plain key." | |
| end | |
| out:write(k.." = ") | |
| persist.write_value(out, v, 0, sub_order) | |
| out:write("\n") | |
| end | |
| return true | |
| end | |
| --- Write a table using Lua table syntax to a writer object. | |
| -- @param out table or userdata: a writer object supporting :write() method. | |
| -- @param tbl table: the table to be written. | |
| local function write_table_as_table(out, tbl) | |
| out:write("return {\n") | |
| for k, v, sub_order in util.sortedpairs(tbl) do | |
| out:write(" ") | |
| write_table_key_assignment(out, k, 1) | |
| persist.write_value(out, v, 1, sub_order) | |
| out:write(",\n") | |
| end | |
| out:write("}\n") | |
| end | |
| --- Save the contents of a table to a string. | |
| -- 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. | |
| -- @param tbl table: the table containing the data to be written | |
| -- @param field_order table: an optional array indicating the order of top-level fields. | |
| -- @return persisted data as string; or nil and an error message | |
| function persist.save_from_table_to_string(tbl, field_order) | |
| local out = {buffer = {}} | |
| function out:write(data) table.insert(self.buffer, data) end | |
| local ok, err = write_table_as_assignments(out, tbl, field_order) | |
| if not ok then | |
| return nil, err | |
| end | |
| return table.concat(out.buffer) | |
| 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. | |
| -- @param filename string: the output filename | |
| -- @param tbl table: the table containing the data to be written | |
| -- @param field_order table: an optional array indicating the order of top-level fields. | |
| -- @return boolean or (nil, string): true if successful, or nil and a | |
| -- message in case of errors. | |
| function persist.save_from_table(filename, tbl, field_order) | |
| local prefix = dir.dir_name(filename) | |
| fs.make_dir(prefix) | |
| local out = io.open(filename, "w") | |
| if not out then | |
| return nil, "Cannot create file at "..filename | |
| end | |
| local ok, err = write_table_as_assignments(out, tbl, field_order) | |
| out:close() | |
| if not ok then | |
| return nil, err | |
| end | |
| return true | |
| end | |
| --- Save the contents of a table as a module. | |
| -- The module contains a 'return' statement that returns the table. | |
| -- Only numbers, strings and tables (containing numbers, strings | |
| -- or other recursively processed tables) are supported. | |
| -- @param filename string: the output filename | |
| -- @param tbl table: the table containing the data to be written | |
| -- @return boolean or (nil, string): true if successful, or nil and a | |
| -- message in case of errors. | |
| function persist.save_as_module(filename, tbl) | |
| local out = io.open(filename, "w") | |
| if not out then | |
| return nil, "Cannot create file at "..filename | |
| end | |
| write_table_as_table(out, tbl) | |
| out:close() | |
| return true | |
| end | |
| function persist.load_config_file_if_basic(filename, cfg) | |
| local env = { | |
| home = cfg.home | |
| } | |
| local result, err, errcode = persist.load_into_table(filename, env) | |
| if errcode == "load" or errcode == "run" then | |
| -- bad config file or depends on env, so error out | |
| return nil, "Could not read existing config file " .. filename | |
| end | |
| local tbl | |
| if errcode == "open" then | |
| -- could not open, maybe file does not exist | |
| tbl = {} | |
| else | |
| tbl = result | |
| tbl.home = nil | |
| end | |
| return tbl | |
| end | |
| function persist.save_default_lua_version(prefix, lua_version) | |
| local ok, err = fs.make_dir(prefix) | |
| if not ok then | |
| return nil, err | |
| end | |
| local fd, err = io.open(dir.path(prefix, "default-lua-version.lua"), "w") | |
| if not fd then | |
| return nil, err | |
| end | |
| fd:write('return "' .. lua_version .. '"\n') | |
| fd:close() | |
| return true | |
| end | |
| return persist |