opus/sys/apis/util.lua

531 lines
10 KiB
Lua

local Util = { }
function Util.tryTimed(timeout, f, ...)
local c = os.clock()
repeat
local ret = f(...)
if ret then
return ret
end
until os.clock()-c >= timeout
end
function Util.tryTimes(attempts, f, ...)
local result
for i = 1, attempts do
result = { f(...) }
if result[1] then
return unpack(result)
end
end
return unpack(result)
end
function Util.print(pattern, ...)
local function serialize(tbl, width)
local str = '{\n'
for k, v in pairs(tbl) do
local value
if type(v) == 'table' then
value = string.format('table: %d', Util.size(v))
else
value = tostring(v)
end
str = str .. string.format(' %s: %s\n', k, value)
end
if #str < width then
str = str:gsub('\n', '') .. ' }'
else
str = str .. '}'
end
return str
end
if type(pattern) == 'string' then
print(string.format(pattern, ...))
elseif type(pattern) == 'table' then
print(serialize(pattern, term.current().getSize()))
else
print(tostring(pattern))
end
end
function Util.runFunction(env, fn, ...)
setfenv(fn, env)
setmetatable(env, { __index = _G })
local args = { ... }
return pcall(function()
return fn(table.unpack(args))
end)
end
-- http://lua-users.org/wiki/SimpleRound
function Util.round(num, idp)
local mult = 10^(idp or 0)
return math.floor(num * mult + 0.5) / mult
end
function Util.random(max, min)
min = min or 0
return math.random(0, max-min) + min
end
--[[ Table functions ]] --
function Util.clear(t)
local keys = Util.keys(t)
for _,k in pairs(keys) do
t[k] = nil
end
end
function Util.empty(t)
return not next(t)
end
function Util.key(t, value)
for k,v in pairs(t) do
if v == value then
return k
end
end
end
function Util.keys(t)
local keys = {}
for k in pairs(t) do
keys[#keys+1] = k
end
return keys
end
function Util.merge(obj, args)
if args then
for k,v in pairs(args) do
obj[k] = v
end
end
end
function Util.deepMerge(obj, args)
if args then
for k,v in pairs(args) do
if type(v) == 'table' then
if not obj[k] then
obj[k] = { }
end
Util.deepMerge(obj[k], v)
else
obj[k] = v
end
end
end
end
function Util.transpose(t)
local tt = { }
for k,v in pairs(t) do
tt[v] = k
end
return tt
end
function Util.find(t, name, value)
for k,v in pairs(t) do
if v[name] == value then
return v, k
end
end
end
function Util.findAll(t, name, value)
local rt = { }
for k,v in pairs(t) do
if v[name] == value then
table.insert(rt, v)
end
end
return rt
end
function Util.shallowCopy(t)
local t2 = {}
for k,v in pairs(t) do
t2[k] = v
end
return t2
end
function Util.deepCopy(t)
if type(t) ~= 'table' then
return t
end
--local mt = getmetatable(t)
local res = {}
for k,v in pairs(t) do
if type(v) == 'table' then
v = Util.deepCopy(v)
end
res[k] = v
end
--setmetatable(res,mt)
return res
end
-- http://snippets.luacode.org/?p=snippets/Filter_a_table_in-place_119
function Util.filterInplace(t, predicate)
local j = 1
for i = 1,#t do
local v = t[i]
if predicate(v) then
t[j] = v
j = j + 1
end
end
while t[j] ~= nil do
t[j] = nil
j = j + 1
end
return t
end
function Util.filter(it, f)
local ot = { }
for k,v in pairs(it) do
if f(k, v) then
ot[k] = v
end
end
return ot
end
function Util.size(list)
if type(list) == 'table' then
local length = 0
table.foreach(list, function() length = length + 1 end)
return length
end
return 0
end
function Util.each(list, func)
for index, value in pairs(list) do
func(value, index, list)
end
end
-- http://stackoverflow.com/questions/15706270/sort-a-table-in-lua
function Util.spairs(t, order)
local keys = Util.keys(t)
-- if order function given, sort by it by passing the table and keys a, b,
-- otherwise just sort the keys
if order then
table.sort(keys, function(a,b) return order(t[a], t[b]) end)
else
table.sort(keys)
end
-- return the iterator function
local i = 0
return function()
i = i + 1
if keys[i] then
return keys[i], t[keys[i]]
end
end
end
function Util.first(t, order)
local keys = Util.keys(t)
if order then
table.sort(keys, function(a,b) return order(t[a], t[b]) end)
else
table.sort(keys)
end
return keys[1], t[keys[1]]
end
--[[ File functions ]]--
function Util.readFile(fname)
local f = fs.open(fname, "r")
if f then
local t = f.readAll()
f.close()
return t
end
end
function Util.writeFile(fname, data)
local file = io.open(fname, "w")
if not file then
error('Unable to open ' .. fname, 2)
end
file:write(data)
file:close()
end
function Util.readLines(fname)
local file = fs.open(fname, "r")
if file then
local t = {}
local line = file.readLine()
while line do
table.insert(t, line)
line = file.readLine()
end
file.close()
return t
end
end
function Util.writeLines(fname, lines)
local file = fs.open(fname, 'w')
if file then
for _,line in ipairs(lines) do
line = file.writeLine(line)
end
file.close()
return true
end
end
function Util.readTable(fname)
local t = Util.readFile(fname)
if t then
return textutils.unserialize(t)
end
end
function Util.writeTable(fname, data)
Util.writeFile(fname, textutils.serialize(data))
end
function Util.loadTable(fname)
local fc = Util.readFile(fname)
if not fc then
return false, 'Unable to read file'
end
local s, m = loadstring('return ' .. fc, fname)
if s then
s, m = pcall(s)
if s then
return m
end
end
return s, m
end
--[[ URL functions ]] --
function Util.download(url, filename)
local h = http.get(url)
if not h then
error('Failed to download ' .. url)
end
local contents = h.readAll()
h.close()
if not contents then
error('Failed to download ' .. url)
end
if filename then
Util.writeFile(filename, contents)
end
return contents
end
function Util.loadUrl(url, env) -- loadfile equivalent
local c = Util.download(url)
return load(c, url, nil, env)
end
function Util.runUrl(env, url, ...) -- os.run equivalent
local fn, m = Util.loadUrl(url, env)
if fn then
local args = { ... }
fn, m = pcall(function() fn(unpack(args)) end)
end
if not fn and m and m ~= '' then
printError(m)
end
return fn, m
end
--[[ String functions ]] --
function Util.toBytes(n)
if n >= 1000000 then
return string.format('%sM', Util.round(n/1000000, 1))
elseif n >= 1000 then
return string.format('%sK', Util.round(n/1000, 1))
end
return tostring(n)
end
function Util.split(str, pattern)
pattern = pattern or "(.-)\n"
local t = {}
local function helper(line) table.insert(t, line) return "" end
helper((str:gsub(pattern, helper)))
return t
end
function Util.matches(str, pattern)
pattern = pattern or '%S+'
local t = { }
for s in str:gmatch(pattern) do
table.insert(t, s)
end
return t
end
function Util.widthify(s, len)
s = s or ''
local slen = #s
if slen < len then
s = s .. string.rep(' ', len - #s)
elseif slen > len then
s = s:sub(1, len)
end
return s
end
-- http://snippets.luacode.org/?p=snippets/trim_whitespace_from_string_76
function Util.trim(s)
return s:find'^%s*$' and '' or s:match'^%s*(.*%S)'
end
-- trim whitespace from left end of string
function Util.triml(s)
return s:match'^%s*(.*)'
end
-- trim whitespace from right end of string
function Util.trimr(s)
return s:find'^%s*$' and '' or s:match'^(.*%S)'
end
-- end http://snippets.luacode.org/?p=snippets/trim_whitespace_from_string_76
-- word wrapping based on:
-- https://www.rosettacode.org/wiki/Word_wrap#Lua and
-- http://lua-users.org/wiki/StringRecipes
local function splittokens(s)
local res = {}
for w in s:gmatch("%S+") do
res[#res+1] = w
end
return res
end
local function paragraphwrap(text, linewidth, res)
linewidth = linewidth or 75
local spaceleft = linewidth
local line = {}
for _, word in ipairs(splittokens(text)) do
if #word + 1 > spaceleft then
table.insert(res, table.concat(line, ' '))
line = { word }
spaceleft = linewidth - #word
else
table.insert(line, word)
spaceleft = spaceleft - (#word + 1)
end
end
table.insert(res, table.concat(line, ' '))
return table.concat(res, '\n')
end
-- end word wrapping
function Util.wordWrap(str, limit)
local longLines = Util.split(str)
local lines = { }
for _,line in ipairs(longLines) do
paragraphwrap(line, limit, lines)
end
return lines
end
-- http://lua-users.org/wiki/AlternativeGetOpt
local function getopt( arg, options )
local tab = {}
for k, v in ipairs(arg) do
if string.sub( v, 1, 2) == "--" then
local x = string.find( v, "=", 1, true )
if x then tab[ string.sub( v, 3, x-1 ) ] = string.sub( v, x+1 )
else tab[ string.sub( v, 3 ) ] = true
end
elseif string.sub( v, 1, 1 ) == "-" then
local y = 2
local l = string.len(v)
local jopt
while ( y <= l ) do
jopt = string.sub( v, y, y )
if string.find( options, jopt, 1, true ) then
if y < l then
tab[ jopt ] = string.sub( v, y+1 )
y = l
else
tab[ jopt ] = arg[ k + 1 ]
end
else
tab[ jopt ] = true
end
y = y + 1
end
end
end
return tab
end
function Util.showOptions(options)
print('Arguments: ')
for k, v in pairs(options) do
print(string.format('-%s %s', v.arg, v.desc))
end
end
function Util.getOptions(options, args, ignoreInvalid)
local argLetters = ''
for _,o in pairs(options) do
if o.type ~= 'flag' then
argLetters = argLetters .. o.arg
end
end
local rawOptions = getopt(args, argLetters)
local argCount = 0
for k,ro in pairs(rawOptions) do
local found = false
for _,o in pairs(options) do
if o.arg == k then
found = true
if o.type == 'number' then
o.value = tonumber(ro)
elseif o.type == 'help' then
Util.showOptions(options)
return false
else
o.value = ro
end
end
end
if not found and not ignoreInvalid then
print('Invalid argument')
Util.showOptions(options)
return false
end
end
return true, Util.size(rawOptions)
end
return Util