diff --git a/utility/.gitignore b/utility/.gitignore new file mode 100644 index 0000000..d907c43 --- /dev/null +++ b/utility/.gitignore @@ -0,0 +1 @@ +*.lua diff --git a/utility/LICENSE b/utility/LICENSE new file mode 100644 index 0000000..29fad8e --- /dev/null +++ b/utility/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Paul Liverman III + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/utility/calc.moon b/utility/calc.moon new file mode 100644 index 0000000..4dbc5a3 --- /dev/null +++ b/utility/calc.moon @@ -0,0 +1,30 @@ +import bytes from require "resty.random" +import byte from string +import floor from math + +-- return a random number between min/max +random = (min, max) -> + unless max + max = min or 1 + min = 0 + + a, b = byte bytes(2), 1, 2 + c, d = byte bytes(2), 1, 2 + value = a + b * 256 + c * 65536 + d * 16777216 -- 0 to 4294967296 + range = max - min + + if max == floor(max) and min == floor(min) + return floor value * range / 4294967296 + min + else + return value * range / 4294967296 + min + +-- map a value within a range of numbers to another range +map = (min1, max1, value, min2, max2) -> + range1 = max1 - min1 + range2 = max2 - min2 + return ((value - min1) * range2 / range1) + min2 + +{ + :random + :map +} diff --git a/utility/datetime.moon b/utility/datetime.moon new file mode 100644 index 0000000..c256061 --- /dev/null +++ b/utility/datetime.moon @@ -0,0 +1,36 @@ +import date, time from os + +-- for database +now = -> + return date "!%Y-%m-%d %X" + +-- database date -> display +pretty_date = (str) -> + year, month, day = str\match "(%d%d%d%d)-(%d%d)-(%d%d)" + return date "%B %d, %Y", time :year, :month, :day + +-- from database +to_seconds = (str) -> + year, month, day, hour, min, sec = str\match "(%d%d%d%d)-(%d%d)-(%d%d) (%d%d):(%d%d):(%d%d)" + return time :year, :month, :day, :hour, :min, :sec + +-- seconds -> for database +for_db = (seconds) -> + return date "!%Y-%m-%d %X", seconds + +gmt_date = -> + return date "!*t" + +gmt_time = -> + return time date "!*t" + +{ + none: "1970-01-01 00:00:00" -- TODO deprecate + zero: "1970-01-01 00:00:00" -- for use in database + :now + :pretty_date + :to_seconds + :for_db + :gmt_date + :gmt_time +} diff --git a/utility/db.moon b/utility/db.moon new file mode 100644 index 0000000..7342b2e --- /dev/null +++ b/utility/db.moon @@ -0,0 +1,9 @@ +escape_similar_to = (str) -> + -- matches any of %_\|*+?{}()[] + -- puts a backslash in front of them + str = str\gsub "[%%_\\|%*%+%?{}%(%)%[%]]", "\\%1" + return str -- return on seperate line to avoid returning 2nd value from gsub + +{ + :escape_similar_to +} diff --git a/utility/fs.moon b/utility/fs.moon new file mode 100644 index 0000000..0901125 --- /dev/null +++ b/utility/fs.moon @@ -0,0 +1,10 @@ +exists = (file_path) -> + handle = io.open file_path, "r" + if handle + handle\close! + return true + return false + +{ + :exists +} diff --git a/utility/gstring.moon b/utility/gstring.moon new file mode 100644 index 0000000..a8f0a6c --- /dev/null +++ b/utility/gstring.moon @@ -0,0 +1,59 @@ +import insert, sort, concat from table +import sub, len from string + +-- splits string by newline into array of strings +lines = (str) -> + tab = {} + for line in str\gmatch "[^\n]+" + insert tab, line + return tab + +-- splits string by spaces into a table of strings +-- (handles bad spacing and duplicate substrings) +split = (str) -> + tab1, tab2 = {}, {} + for word in str\gmatch "%S+" + tab1[word] = true + for word in pairs tab1 + insert tab2, word + return tab2 + +-- splits string by commas into a table of strings +-- (expects a well-formated string!) +comma_split = (str) -> + tab = {} + for word in str\gmatch "[^,]+" + insert tab, word + return tab + +-- bool: does str1 start with str2 +starts = (str1, str2) -> + return str2 == sub str1, 1, len str2 + +-- takes space-separated string and puts it in alphabetical order +-- (handles weird spacing, returns with single-spacing) +alphabetize = (str) -> + tab = split str + sort tab + return concat tab, " " + +-- takes space-separated string and removes duplicate entries from it +remove_duplicates = (str) -> + strings = split str + tab, result = {}, {} + + for str in *strings + tab[str] = true + for str in pairs tab + insert result, str + + return concat result, " " + +{ + :lines + :split + :comma_split + :starts + :alphabetize + :remove_duplicates +} diff --git a/utility/gtable.moon b/utility/gtable.moon new file mode 100644 index 0000000..f9b9505 --- /dev/null +++ b/utility/gtable.moon @@ -0,0 +1,33 @@ +-- appends n arrays to the end of the first array +append = (tab1, ...) -> + for n = 1, select "#", ... + tab2 = select n, ... + for i = 1, #tab2 + tab1[#tab1+1] = tab2[i] + return tab1 + +-- returns a new table shallow copying data from all arguments +-- later arguments overwrite any keys in earlier arguments +-- ignores non-table arguments (skipping them) +shallow_copy = (...) -> + new = {} + for n = 1, select "#", ... + tab = select n, ... + if "table" == type tab + for k,v in pairs tab + new[k] = v + return new + +-- returns a new table with flipped keys and values +invert = (tab) -> + new = {} + for key, value in pairs tab + new[value] = key + return new + +{ + :append + :shallow_copy + shallow_merge: shallow_copy -- TODO deprecate + :invert +} diff --git a/utility/migrations.moon b/utility/migrations.moon new file mode 100644 index 0000000..21cad91 --- /dev/null +++ b/utility/migrations.moon @@ -0,0 +1,13 @@ +import create_table, types, create_index from require "lapis.db.schema" + +{ + [1518948992]: => + create_table "settings", { + {"name", types.varchar primary_key: true, unique: true} + {"value", types.text null: true} + + {"created_at", types.time} + {"updated_at", types.time} + } + create_index "settings", "name", unique: true +} diff --git a/utility/models/Settings.moon b/utility/models/Settings.moon new file mode 100644 index 0000000..25ae17b --- /dev/null +++ b/utility/models/Settings.moon @@ -0,0 +1,5 @@ +import Model from require "lapis.db.model" + +class Settings extends Model + @timestamp: true + @primary_key: "name" diff --git a/utility/settings.moon b/utility/settings.moon new file mode 100644 index 0000000..d2ff831 --- /dev/null +++ b/utility/settings.moon @@ -0,0 +1,110 @@ +import Settings from require "models" + +totype = (str) -> + if value = tonumber str + return value + if str == "true" + return true + if str == "false" + return false + if str == "nil" + return nil + return str + +cache = {} + +get = (name, create=true) -> + setting = cache[name] + unless setting + setting = Settings\find :name + if (not setting) and create + setting = Settings\create :name + cache[name] = setting + + if setting + return setting + else + return nil, "failed to load '#{name}' setting" + +local settings +settings = { + get: (name, skip_index) -> + unless name + return settings.load! + + unless skip_index -- for metamethods to not loop endlessly + return settings[name] if settings[name] + + setting, err = get name + if setting + value = totype setting.value + settings[name] = value + return value + else + return nil, err + + set: (name, value) -> + unless name + return settings.save! + + setting, err = get name + if setting + settings[name] = value + return setting\update value: tostring value + else + return nil, err + + save: (name) -> + if name + setting, err = get name + if setting + return setting\update value: tostring settings[name] + else + return nil, err + + else + for name, value in pairs settings + switch name + when "get", "set", "save", "load", "delete" + nil + else + t = type value + if t == "function" or t == "table" + return nil, "cannot save '#{name}' setting, type '#{t}' not supported" + else + unless cache[name] + cache[name] = Settings\find :name + unless cache[name] + cache[name] = Settings\create :name + + for name, setting in pairs cache + _, err = setting\update value: tostring settings[name] + return nil, err if err + return true + + load: (name) -> + return settings.get name if name + + all_settings = Settings\select "WHERE true" + for setting in *all_settings + name = setting.name + cache[name] = setting + settings[name] = totype setting.value + return settings + + delete: (name) -> + if setting = get name, false + if setting\delete! + cache[name] = nil + settings[name] = nil + else + return nil, "failed to delete '#{name}' setting" + return true +} + +return setmetatable settings, { + __call: (t, name) -> + return settings.get name, true + __index: (t, name) -> + return settings.get name, true + } diff --git a/utility/shell.moon b/utility/shell.moon new file mode 100644 index 0000000..07a8e16 --- /dev/null +++ b/utility/shell.moon @@ -0,0 +1,23 @@ +quote = (str) -> + escaped = str\gsub "'", [['"'"']] + return "'#{escaped}'" + +execute = (cmd, capture_exit_code=true) -> + local handle + if capture_exit_code + handle = io.popen "#{cmd}\necho $?" + else + handle = io.popen cmd + result = handle\read "*a" + handle\close! + + exit_start, exit_end = result\find "(%d*)[%c]$" + exit_code = tonumber result\sub(exit_start, exit_end)\sub 1, -2 + output = result\sub 1, exit_start - 1 + + return exit_code, output + +{ + :quote + :execute +}