1
0
mirror of https://github.com/kepler155c/opus synced 2024-11-15 05:04:50 +00:00
opus/sys/apis/util.lua

711 lines
13 KiB
Lua
Raw Normal View History

2016-12-11 19:24:52 +00:00
local Util = { }
2017-10-09 00:03:01 +00:00
local fs = _G.fs
local http = _G.http
local os = _G.os
local term = _G.term
local textutils = _G.textutils
2016-12-11 19:24:52 +00:00
function Util.tryTimed(timeout, f, ...)
2018-01-24 22:39:38 +00:00
local c = os.clock()
repeat
local ret = f(...)
if ret then
return ret
end
until os.clock()-c >= timeout
2016-12-11 19:24:52 +00:00
end
function Util.tryTimes(attempts, f, ...)
2018-01-24 22:39:38 +00:00
local result
for _ = 1, attempts do
result = { f(...) }
if result[1] then
return unpack(result)
end
end
return unpack(result)
2016-12-11 19:24:52 +00:00
end
2017-05-14 04:53:59 +00:00
function Util.throttle(fn)
2018-01-24 22:39:38 +00:00
local ts = os.clock()
local timeout = .095
return function(...)
local nts = os.clock()
if nts > ts + timeout then
os.sleep(0)
ts = os.clock()
if fn then
fn(...)
end
end
end
2017-05-14 03:45:59 +00:00
end
2017-05-09 05:57:00 +00:00
function Util.tostring(pattern, ...)
2016-12-11 19:24:52 +00:00
2018-01-24 22:39:38 +00:00
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
return string.format(pattern, ...)
elseif type(pattern) == 'table' then
return serialize(pattern, term.current().getSize())
end
return tostring(pattern)
2017-05-09 05:57:00 +00:00
end
function Util.print(pattern, ...)
2018-01-24 22:39:38 +00:00
print(Util.tostring(pattern, ...))
2016-12-11 19:24:52 +00:00
end
function Util.getVersion()
2018-01-24 22:39:38 +00:00
local version
2018-01-24 22:39:38 +00:00
if _G._CC_VERSION then
version = tonumber(_G._CC_VERSION:match('[%d]+%.?[%d][%d]'))
end
if not version and _G._HOST then
version = tonumber(_G._HOST:match('[%d]+%.?[%d][%d]'))
end
2018-01-24 22:39:38 +00:00
return version or 1.7
end
2017-10-12 21:58:35 +00:00
function Util.getMinecraftVersion()
2018-01-24 22:39:38 +00:00
local mcVersion = _G._MC_VERSION or 'unknown'
if _G._HOST then
local version = _G._HOST:match('%S+ %S+ %((%S.+)%)')
if version then
mcVersion = version:match('Minecraft (%S+)') or version
end
end
return mcVersion
2017-10-12 21:58:35 +00:00
end
function Util.checkMinecraftVersion(minVersion)
2018-01-24 22:39:38 +00:00
local version = Util.getMinecraftVersion()
local function convert(v)
local m1, m2, m3 = v:match('(%d)%.(%d)%.?(%d?)')
return tonumber(m1) * 10000 + tonumber(m2) * 100 + (tonumber(m3) or 0)
end
2017-10-12 21:58:35 +00:00
2018-01-24 22:39:38 +00:00
return convert(version) >= convert(tostring(minVersion))
2017-10-12 21:58:35 +00:00
end
function Util.signum(num)
if num > 0 then
return 1
elseif num < 0 then
return -1
else
return 0
end
end
2019-02-23 23:36:17 +00:00
function Util.clamp(num, low, high)
return num < low and low or num > high and high or num
end
2016-12-11 19:24:52 +00:00
-- http://lua-users.org/wiki/SimpleRound
function Util.round(num, idp)
2018-01-24 22:39:38 +00:00
local mult = 10^(idp or 0)
2018-12-09 19:52:52 +00:00
return Util.signum(num) * math.floor(math.abs(num) * mult + 0.5) / mult
2016-12-11 19:24:52 +00:00
end
function Util.randomFloat(max, min)
2018-01-24 22:39:38 +00:00
min = min or 0
max = max or 1
return (max-min) * math.random() + min
2016-12-11 19:24:52 +00:00
end
--[[ Table functions ]] --
function Util.clear(t)
2018-01-24 22:39:38 +00:00
local keys = Util.keys(t)
for _,k in pairs(keys) do
t[k] = nil
end
2016-12-11 19:24:52 +00:00
end
function Util.empty(t)
2018-01-24 22:39:38 +00:00
return not next(t)
2016-12-11 19:24:52 +00:00
end
function Util.key(t, value)
2018-01-24 22:39:38 +00:00
for k,v in pairs(t) do
if v == value then
return k
end
end
2016-12-11 19:24:52 +00:00
end
function Util.keys(t)
2018-01-24 22:39:38 +00:00
local keys = { }
for k in pairs(t) do
keys[#keys+1] = k
end
return keys
2016-12-11 19:24:52 +00:00
end
function Util.merge(obj, args)
2018-01-24 22:39:38 +00:00
if args then
for k,v in pairs(args) do
obj[k] = v
end
end
return obj
2016-12-11 19:24:52 +00:00
end
function Util.deepMerge(obj, args)
2018-01-24 22:39:38 +00:00
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
2016-12-11 19:24:52 +00:00
end
function Util.transpose(t)
2018-01-24 22:39:38 +00:00
local tt = { }
for k,v in pairs(t) do
tt[v] = k
end
return tt
2016-12-11 19:24:52 +00:00
end
2017-10-18 23:51:55 +00:00
function Util.contains(t, value)
2018-01-24 22:39:38 +00:00
for k,v in pairs(t) do
if v == value then
return k
end
end
2017-10-18 23:51:55 +00:00
end
2016-12-11 19:24:52 +00:00
function Util.find(t, name, value)
2018-01-24 22:39:38 +00:00
for k,v in pairs(t) do
if v[name] == value then
return v, k
end
end
2016-12-11 19:24:52 +00:00
end
function Util.findAll(t, name, value)
2018-01-24 22:39:38 +00:00
local rt = { }
for _,v in pairs(t) do
if v[name] == value then
table.insert(rt, v)
end
end
return rt
2016-12-11 19:24:52 +00:00
end
function Util.shallowCopy(t)
2018-11-06 21:43:24 +00:00
if not t then error('Util.shallowCopy: invalid table', 2) end
2018-01-24 22:39:38 +00:00
local t2 = { }
for k,v in pairs(t) do
t2[k] = v
end
return t2
2016-12-11 19:24:52 +00:00
end
function Util.deepCopy(t)
2018-01-24 22:39:38 +00:00
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
2016-12-11 19:24:52 +00:00
end
-- http://snippets.luacode.org/?p=snippets/Filter_a_table_in-place_119
function Util.filterInplace(t, predicate)
2018-01-24 22:39:38 +00:00
local j = 1
2016-12-11 19:24:52 +00:00
2018-01-24 22:39:38 +00:00
for i = 1,#t do
local v = t[i]
if predicate(v) then
t[j] = v
j = j + 1
end
end
2016-12-11 19:24:52 +00:00
2018-01-24 22:39:38 +00:00
while t[j] ~= nil do
t[j] = nil
j = j + 1
end
2016-12-11 19:24:52 +00:00
2018-01-24 22:39:38 +00:00
return t
2016-12-11 19:24:52 +00:00
end
function Util.filter(it, f)
2018-01-24 22:39:38 +00:00
local ot = { }
for k,v in pairs(it) do
if f(v) then
ot[k] = v
end
end
return ot
2016-12-11 19:24:52 +00:00
end
2018-12-21 00:28:26 +00:00
function Util.reduce(t, fn, acc)
2019-02-26 13:17:53 +00:00
acc = acc or 0
2018-12-21 00:28:26 +00:00
for _, v in pairs(t) do
2019-02-26 13:17:53 +00:00
acc = fn(acc, v)
2018-12-21 00:28:26 +00:00
end
return acc
end
2016-12-11 19:24:52 +00:00
function Util.size(list)
2018-01-24 22:39:38 +00:00
if type(list) == 'table' then
local length = 0
for _ in pairs(list) do
length = length + 1
end
return length
end
return 0
2016-12-11 19:24:52 +00:00
end
2018-12-24 00:49:28 +00:00
local function isArray(value)
-- dubious
return type(value) == "table" and (value[1] or next(value) == nil)
end
function Util.removeByValue(t, e)
2018-01-24 22:39:38 +00:00
for k,v in pairs(t) do
if v == e then
2018-12-24 00:49:28 +00:00
if isArray(t) then
table.remove(t, k)
else
t[k] = nil
end
2018-01-24 22:39:38 +00:00
break
end
end
end
2019-01-02 08:04:24 +00:00
function Util.any(t, fn)
for _,v in pairs(t) do
if fn(v) then
return true
end
end
end
2018-12-24 00:49:28 +00:00
function Util.every(t, fn)
for _,v in pairs(t) do
if not fn(v) then
return false
end
end
return true
end
2016-12-11 19:24:52 +00:00
function Util.each(list, func)
2018-01-24 22:39:38 +00:00
for index, value in pairs(list) do
func(value, index, list)
end
2016-12-11 19:24:52 +00:00
end
2017-10-20 08:23:17 +00:00
function Util.rpairs(t)
2018-01-24 22:39:38 +00:00
local tkeys = Util.keys(t)
local i = #tkeys
return function()
local key = tkeys[i]
local k,v = key, t[key]
i = i - 1
if v then
return k, v
end
end
2017-10-20 08:23:17 +00:00
end
2016-12-11 19:24:52 +00:00
-- http://stackoverflow.com/questions/15706270/sort-a-table-in-lua
function Util.spairs(t, order)
2018-01-24 22:39:38 +00:00
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
2016-12-11 19:24:52 +00:00
end
function Util.first(t, order)
2018-01-24 22:39:38 +00:00
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]]
2016-12-11 19:24:52 +00:00
end
--[[ File functions ]]--
function Util.readFile(fname)
2018-01-24 22:39:38 +00:00
local f = fs.open(fname, "r")
if f then
local t = f.readAll()
f.close()
return t
end
2016-12-11 19:24:52 +00:00
end
function Util.writeFile(fname, data)
2018-10-30 02:03:46 +00:00
if not fname or not data then error('Util.writeFile: invalid parameters', 2) end
2018-01-24 22:39:38 +00:00
local file = io.open(fname, "w")
if not file then
error('Unable to open ' .. fname, 2)
end
file:write(data)
file:close()
2016-12-11 19:24:52 +00:00
end
function Util.readLines(fname)
2018-01-24 22:39:38 +00:00
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
2016-12-11 19:24:52 +00:00
end
function Util.writeLines(fname, lines)
2018-01-24 22:39:38 +00:00
local file = fs.open(fname, 'w')
if file then
for _,line in ipairs(lines) do
file.writeLine(line)
end
file.close()
return true
end
2016-12-11 19:24:52 +00:00
end
function Util.readTable(fname)
2018-01-24 22:39:38 +00:00
local t = Util.readFile(fname)
if t then
return textutils.unserialize(t)
end
2016-12-11 19:24:52 +00:00
end
function Util.writeTable(fname, data)
2018-01-24 22:39:38 +00:00
Util.writeFile(fname, textutils.serialize(data))
2016-12-11 19:24:52 +00:00
end
function Util.loadTable(fname)
2018-01-24 22:39:38 +00:00
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
2016-12-11 19:24:52 +00:00
end
2017-09-27 19:42:40 +00:00
--[[ loading and running functions ]] --
2017-10-09 00:03:01 +00:00
function Util.httpGet(url, headers)
2018-01-24 22:39:38 +00:00
local h, msg = http.get(url, headers)
if h then
local contents = h.readAll()
h.close()
return contents
end
return h, msg
2017-10-09 00:03:01 +00:00
end
function Util.download(url, filename)
2018-01-24 22:39:38 +00:00
local contents, msg = Util.httpGet(url)
if not contents then
2018-11-03 22:13:41 +00:00
error(string.format('Failed to download %s\n%s', url, msg), 2)
2018-01-24 22:39:38 +00:00
end
2016-12-11 19:24:52 +00:00
2018-01-24 22:39:38 +00:00
if filename then
Util.writeFile(filename, contents)
end
return contents
2016-12-11 19:24:52 +00:00
end
function Util.loadUrl(url, env) -- loadfile equivalent
2018-01-24 22:39:38 +00:00
local c, msg = Util.httpGet(url)
if not c then
return c, msg
end
return load(c, url, nil, env)
2016-12-11 19:24:52 +00:00
end
function Util.runUrl(env, url, ...) -- os.run equivalent
2018-01-24 22:39:38 +00:00
setmetatable(env, { __index = _G })
local fn, m = Util.loadUrl(url, env)
if fn then
return pcall(fn, ...)
end
return fn, m
2017-09-26 19:18:44 +00:00
end
function Util.run(env, path, ...)
2018-01-24 22:39:38 +00:00
if type(env) ~= 'table' then error('Util.run: env must be a table', 2) end
setmetatable(env, { __index = _G })
local fn, m = loadfile(path, env)
if fn then
return pcall(fn, ...)
end
return fn, m
2016-12-11 19:24:52 +00:00
end
2017-09-27 19:42:40 +00:00
function Util.runFunction(env, fn, ...)
2018-01-24 22:39:38 +00:00
setfenv(fn, env)
setmetatable(env, { __index = _G })
return pcall(fn, ...)
2017-09-27 19:42:40 +00:00
end
2016-12-11 19:24:52 +00:00
--[[ String functions ]] --
function Util.toBytes(n)
2018-01-24 22:39:38 +00:00
if not tonumber(n) then error('Util.toBytes: n must be a number', 2) end
if n >= 1000000 or n <= -1000000 then
return string.format('%sM', math.floor(n/1000000 * 10) / 10)
elseif n >= 10000 or n <= -10000 then
return string.format('%sK', math.floor(n/1000))
elseif n >= 1000 or n <= -1000 then
return string.format('%sK', math.floor(n/1000 * 10) / 10)
end
return tostring(n)
2016-12-11 19:24:52 +00:00
end
2017-10-09 00:03:01 +00:00
function Util.insertString(str, istr, pos)
2018-01-24 22:39:38 +00:00
return str:sub(1, pos - 1) .. istr .. str:sub(pos)
2016-12-27 03:26:43 +00:00
end
2016-12-11 19:24:52 +00:00
function Util.split(str, pattern)
2018-10-30 02:03:46 +00:00
if not str then error('Util.split: Invalid parameters', 2) end
2018-01-24 22:39:38 +00:00
pattern = pattern or "(.-)\n"
local t = {}
local function helper(line) table.insert(t, line) return "" end
helper((str:gsub(pattern, helper)))
return t
2016-12-11 19:24:52 +00:00
end
function Util.matches(str, pattern)
2018-01-24 22:39:38 +00:00
pattern = pattern or '%S+'
local t = { }
for s in str:gmatch(pattern) do
table.insert(t, s)
end
return t
2016-12-11 19:24:52 +00:00
end
2018-11-04 18:00:37 +00:00
function Util.startsWith(s, match)
2018-01-24 22:39:38 +00:00
return string.sub(s, 1, #match) == match
2017-09-15 05:08:04 +00:00
end
2016-12-11 19:24:52 +00:00
function Util.widthify(s, len)
2018-01-24 22:39:38 +00:00
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
2016-12-11 19:24:52 +00:00
end
-- http://snippets.luacode.org/?p=snippets/trim_whitespace_from_string_76
function Util.trim(s)
2018-01-24 22:39:38 +00:00
return s:find'^%s*$' and '' or s:match'^%s*(.*%S)'
2016-12-11 19:24:52 +00:00
end
2017-10-08 21:45:01 +00:00
2016-12-11 19:24:52 +00:00
-- trim whitespace from left end of string
function Util.triml(s)
2018-01-24 22:39:38 +00:00
return s:match'^%s*(.*)'
2016-12-11 19:24:52 +00:00
end
2017-10-08 21:45:01 +00:00
2016-12-11 19:24:52 +00:00
-- trim whitespace from right end of string
function Util.trimr(s)
2018-01-24 22:39:38 +00:00
return s:find'^%s*$' and '' or s:match'^(.*%S)'
2016-12-11 19:24:52 +00:00
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 paragraphwrap(text, linewidth, res)
2018-01-24 22:39:38 +00:00
linewidth = linewidth or 75
local spaceleft = linewidth
local line = { }
for word in text:gmatch("%S+") do
local len = #word + 1
--if colorMode then
-- word:gsub('()@([@%d])', function(pos, c) len = len - 2 end)
--end
if len > spaceleft then
table.insert(res, table.concat(line, ' '))
line = { word }
spaceleft = linewidth - len - 1
else
table.insert(line, word)
spaceleft = spaceleft - len
end
end
table.insert(res, table.concat(line, ' '))
return table.concat(res, '\n')
2016-12-11 19:24:52 +00:00
end
-- end word wrapping
function Util.wordWrap(str, limit)
2018-01-24 22:39:38 +00:00
local longLines = Util.split(str)
local lines = { }
2016-12-11 19:24:52 +00:00
2018-01-24 22:39:38 +00:00
for _,line in ipairs(longLines) do
paragraphwrap(line, limit, lines)
end
2016-12-11 19:24:52 +00:00
2018-01-24 22:39:38 +00:00
return lines
2016-12-11 19:24:52 +00:00
end
function Util.args(arg)
2018-01-24 22:39:38 +00:00
local options, args = { }, { }
local k = 1
while k <= #arg do
local v = arg[k]
if string.sub(v, 1, 1) == '-' then
local opt = string.sub(v, 2)
options[opt] = arg[k + 1]
k = k + 1
else
table.insert(args, v)
end
k = k + 1
end
return options, args
end
2018-01-13 20:17:26 +00:00
2016-12-11 19:24:52 +00:00
-- http://lua-users.org/wiki/AlternativeGetOpt
local function getopt( arg, options )
2018-01-24 22:39:38 +00:00
local tab = {}
for k, v in ipairs(arg) do
if type(v) == 'string' then
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
end
return tab
2016-12-11 19:24:52 +00:00
end
function Util.showOptions(options)
2018-01-24 22:39:38 +00:00
print('Arguments: ')
for _, v in pairs(options) do
print(string.format('-%s %s', v.arg, v.desc))
end
2016-12-11 19:24:52 +00:00
end
function Util.getOptions(options, args, ignoreInvalid)
2018-01-24 22:39:38 +00:00
local argLetters = ''
for _,o in pairs(options) do
if o.type ~= 'flag' then
argLetters = argLetters .. o.arg
end
end
local rawOptions = getopt(args, argLetters)
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)
2016-12-11 19:24:52 +00:00
end
return Util