From cad8807753d3f3a5b94185866c6fa9de6172604c Mon Sep 17 00:00:00 2001 From: perro Date: Fri, 16 Jun 2023 19:39:17 -0700 Subject: [PATCH] Finally a more reliable way to extend functions --- README.md | 21 ++- lua-dog-1.0-0.rockspec | 7 +- src/dog.lua | 364 ++++++++++++++++++++++------------------- 3 files changed, 223 insertions(+), 169 deletions(-) diff --git a/README.md b/README.md index 317b57c..d14960e 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,30 @@ # Lua Dog -Adds functions to Lua Standard Libraries. +Adds functions to Lua Standard Libraries and Pandoc Library. ## Install luarocks install lua-dog +## Usage + +You can use Lua Dog in 2 ways. + +The laziest way is: + + local dog = require("dog") + dog.import() + -- Lua Dog 'dog.os.uname()' function was imported as 'os.uname()' + os.uname() + +This will import all Lua Dog functions to the Lua Standard Libraries and Pandoc Library. + +If you prefer not to import, the way is: + + require "dog" + -- Lua Dog 'dog.os.uname()' function was NOT imported as 'os.uname()' + dog.os.uname() + ## Functions Check `src`. diff --git a/lua-dog-1.0-0.rockspec b/lua-dog-1.0-0.rockspec index b774e47..434c4e2 100644 --- a/lua-dog-1.0-0.rockspec +++ b/lua-dog-1.0-0.rockspec @@ -1,12 +1,13 @@ package = "lua-dog" -version = "1.0-0" +version = "1.1.0" source = { - url = "https://gitlab.com/perritotuerto/codigo/lua-dog/-/archive/v1.0.0/lua-dog-v1.0.0.tar.gz", + url = "https://gitlab.com/perritotuerto/codigo/lua-dog/-/archive/v" .. + version .. "/lua-dog-v" .. version .. ".tar.gz", } description = { summary = "Lua extensions for lazy dogs", detailed = [[ - Adds functions to Lua Standard Libraries. + Extends Lua Standard Libraries and Pandoc Library. ]], homepage = "https://gitlab.com/perritotuerto/codigo/lua-dog/", license = "GPLv3", diff --git a/src/dog.lua b/src/dog.lua index 53e53b0..590f1b0 100644 --- a/src/dog.lua +++ b/src/dog.lua @@ -1,7 +1,38 @@ +-- Variable for storing all the functions +local dog = {} + +-- Functions are divided according to the standard library to extend +dog.io = {} +dog.os = {} +dog.string = {} +dog.utf8 = {} + +-- Imports dog functions to _G +-- Allows using the other functions without a prefix, i.e.: +-- dog.os.uname() => os.uname() +function dog.import() + local msg = "[WARNING] [DOG] " + msg = msg .. "Ignoring import: '@1' already exists; use '@2' instead" + for libkey, dogfns in pairs(dog) do + local lib = _G[libkey] + if type(dogfns) == "table" then + for fnkey, fn in pairs(dogfns) do + if not(lib[fnkey]) then + lib[fnkey] = fn + else + local name = libkey .. "." .. fnkey .. "()" + msg = msg:gsub("@1", name):gsub("@2", "dog." .. name) + print(msg) + end + end + end + end +end + -- Tries popen -- @param ... string: Chunks for popen -- @return boolean, string: Status and output of popen -function io.try(...) +function dog.io.try(...) local cmd = table.concat({...}, " ") .. " 2>&1" local handle = io.popen(cmd) local output = handle:read("*a") @@ -12,13 +43,13 @@ end -- Gets OS short name -- It is just intented to know if it is Linux, macOS or Windows. -- @return string: "linux" or "bsd" or "macos" or "windows" -function os.uname() - local status, output = io.try("uname") +function dog.os.uname() + local status, output = dog.io.try("uname") if status then output = output:gsub(" .*", ""):lower() if output ~= "linux" and output:match("bsd") ~= nil then return "bsd" - elseif out ~= "linux" then + elseif output ~= "linux" then return "macos" end return "linux" @@ -28,19 +59,19 @@ end -- Checks if OS is windows -- @return boolean: Windows or not -function os.iswin() - return os.uname() == "windows" +function dog.os.iswin() + return dog.os.uname() == "windows" end -- Checks if OS is Unix -- @return boolean: Unix or not -function os.isunix() - return os.uname() ~= "windows" +function dog.os.isunix() + return dog.os.uname() ~= "windows" end -- Gets OS language -- @return string, string, string: Language, locale and encoding -function os.lang() +function dog.os.lang() local lang = os.getenv("LANG") if lang ~= nil then return lang:match("(%w%w)_?(%w?%w?)%.?(.*)") @@ -48,10 +79,158 @@ function os.lang() return "en", "US", "UTF-8" end +-- Checks if string is empty +-- @return boolean: Empty or not +function dog.string.isempty(str) + return str == '' +end + +-- Changes newlines so everything is in one line +-- @return string: String with formatted newlines as literal "\n" +function dog.string.linearize(str) + return str:gsub("\n", "\\n") +end + +-- Normalizes string +-- @return string: Normalized string +function dog.string.normalize(str) + str = str:lower() + for newchar, chars in pairs(dog.utf8.table()) do + for _, oldchar in ipairs(chars) do + str = str:gsub(oldchar, newchar) + end + end + return str +end + +-- Adds indent +-- @param num number: Indent size, 2 by default +-- @param char string: Indent character, space by default +-- @return strin: Indented string +function dog.string.indent(str, num, char) + num = num or 2 + char = char or " " + char = string.rep(char, num) + return char .. str:gsub("\n", "\n" .. char) +end + +-- Splits the string +-- Note: only support splitting by one character. +-- Could be solved by slicing with find. +-- @param sep string: String separator, space by default +-- @return table: String matches +function dog.string.split(str, sep) + sep = sep or "%s+" + local parts = {} + for part in str:gmatch("([^" .. sep .. "]+)") do + table.insert(parts, part) + end + return parts +end + +-- Removes spaces at the beginning of the string +-- @return string: Left stripped string +function dog.string.lstrip(str) + return str:gsub("^%s+", "") +end + +-- Removes spaces at the end of the string +-- @return string: Right stripped string +function dog.string.rstrip(str) + return str:gsub("%s+$", "") +end + +-- Removes spaces at the beginning and at the end of the string +-- @return string: Stripped string +function dog.string.strip(str) + str = dog.string.lstrip(str) + return dog.string.rstrip(str) +end + +-- Alias of strip +function dog.string.trim(str) + return dog.string.strip(str) +end + +-- The following are heavily influenced by Python pathlib +-- Check: https://docs.python.org/3/library/pathlib.html + +-- Checks if string is a file or a directory +-- @return boolean: Exists or not +function dog.string.exists(str) + return os.rename(str, str) ~= nil +end + +-- Checks if string is a file +-- @return boolean: File or not +function dog.string.isfile(str) + if dog.string.exists(str) then + return io.open(str, "a+") ~= nil + end + return false +end + +-- Checks if string is a directory +-- @return boolean: Directory or not +function dog.string.isdir(str) + if dog.string.exists(str) then + return io.open(str, "a+") == nil + elseif str == "." or str == ".." then + return true + end + return false +end + +-- Reads file content as string +-- @return string or nil: File as string or nil +function dog.string.readtext(str) + if dog.string.exists(str) then + return io.open(str):read("*a") + end +end + +-- Read file content as lines +-- @return table: Table of file lines or nil +function dog.string.readlines(str) + local lines = {} + if dog.string.exists(str) then + for line in io.open(str):lines() do + table.insert(lines, line) + end + end + return lines +end + +-- Gets file without suffix +-- @return string: File wihtout suffix +function dog.string.stem(str) + return str:gsub("%.%a+$", "") +end + +-- Gets file extensions +-- @return table: List of file extensions +function dog.string.suffixes(str) + local suffixes = {} + for suffix in str:gmatch("%.%a+") do + table.insert(suffixes, suffix) + end + return suffixes +end + +-- Gets file final extension +-- @return string: Final file extension +function dog.string.suffix(str) + local suffixes = str:suffixes() + if suffixes[#suffixes] then + return suffixes[#suffixes] + end + return "" +end + -- Gets an equivalency table between ASCII and Unicode -- Note: filled on demand. -- @return table: ASCII-Unicode table equivalency -function utf8.table() +function dog.utf8.table() return { ["a"] = {"á", "à", "ä"}, ["e"] = {"é", "è", "ë"}, @@ -62,155 +241,7 @@ function utf8.table() } end --- Checks if string is empty --- @return boolean: Empty or not -function string:isempty() - return self == '' -end - --- Changes newlines so everything is in one line --- @return string: String with formatted newlines as literal "\n" -function string:linearize() - return self:gsub("\n", "\\n") -end - --- Normalizes string --- @return string: Normalized string -function string:normalize() - self = self:lower() - for newchar, chars in pairs(utf8.table()) do - for _, oldchar in ipairs(chars) do - self = self:gsub(oldchar, newchar) - end - end - return self -end - --- Adds indent --- @param num number: Indent size, 2 by default --- @param char string: Indent character, space by default --- @return strin: Indented string -function string:indent(num, char) - num = num or 2 - char = char or " " - char = string.rep(char, num) - return char .. self:gsub("\n", "\n" .. char) -end - --- Splits the string --- Note: only support splitting by one character. --- Could be solved by slicing with find. --- @param sep string: String separator, space by default --- @return table: String matches -function string:split(sep) - sep = sep or "%s+" - local parts = {} - for part in self:gmatch("([^" .. sep .. "]+)") do - table.insert(parts, part) - end - return parts -end - --- Removes spaces at the beginning of the string --- @return string: Left stripped string -function string:lstrip() - return self:gsub("^%s+", "") -end - --- Removes spaces at the end of the string --- @return string: Right stripped string -function string:rstrip() - return self:gsub("%s+$", "") -end - --- Removes spaces at the beginning and at the end of the string --- @return string: Stripped string -function string:strip() - self = self:lstrip():rstrip() - return self -end - --- Alias of strip -function string:trim() - return self:strip() -end - --- The following are heavily influenced by Python pathlib --- Check: https://docs.python.org/3/library/pathlib.html - --- Checks if string is a file or a directory --- @return boolean: Exists or not -function string:exists() - return os.rename(self, self) ~= nil -end - --- Checks if string is a file --- @return boolean: File or not -function string:isfile() - if self:exists() then - return io.open(self, "a+") ~= nil - end - return false -end - --- Checks if string is a directory --- @return boolean: Directory or not -function string:isdir() - if self:exists() then - return io.open(self, "a+") == nil - elseif self == "." or self == ".." then - return true - end - return false -end - --- Reads file content as string --- @return string or nil: File as string or nil -function string:readtext() - if self:exists() then - return io.open(self):read("*a") - end -end - --- Read file content as lines --- @return table: Table of file lines or nil -function string:readlines() - local lines = {} - if self:exists() then - for line in io.open(self):lines() do - table.insert(lines, line) - end - end - return lines -end - --- Gets file without suffix --- @return string: File wihtout suffix -function string:stem() - return self:gsub("%.%a+$", "") -end - --- Gets file extensions --- @return table: List of file extensions -function string:suffixes() - local suffixes = {} - for suffix in self:gmatch("%.%a+") do - table.insert(suffixes, suffix) - end - return suffixes -end - --- Gets file final extension --- @return string: Final file extension -function string:suffix() - local suffixes = self:suffixes() - if suffixes[#suffixes] then - return suffixes[#suffixes] - end - return "" -end - --- Requires Pandoc +-- Extends Pandoc Library -- Check: https://pandoc.org/lua-filters.html#module-pandoc if pandoc ~= nil then @@ -222,8 +253,8 @@ if pandoc ~= nil then -- @param file string: File name -- @return string: File extension according to Pandoc format namespaces function pandoc.getext(file) - local ext = file:suffix():gsub("^.", "") - return ((ext == "md" or ext:isempty()) and "markdown" or ext) + local ext = dog.string.suffix(file):gsub("^.", "") + return ((ext == "md" or dog.string.isempty(ext)) and "markdown" or ext) end -- Pandoc converter @@ -237,9 +268,10 @@ if pandoc ~= nil then -- @return string: Output file content function pandoc.convert(ifile, oformat, ofile, iformat) iformat = (iformat == nil and pandoc.getext(ifile) or iformat) - local doc = pandoc.write(pandoc.read(ifile:readtext(), iformat), oformat) + local itext = pandoc.read(dog.string.readtext(ifile), iformat) + local doc = pandoc.write(itext, oformat) if ofile ~= nil then - local eol = (os:isunix() and "\n" or "\r\n") + local eol = (dog.os:isunix() and "\n" or "\r\n") io.open(ofile, "w"):write(doc, eol):close() end return doc @@ -248,7 +280,7 @@ if pandoc ~= nil then -- Stringifies Pandoc content -- Avoids undesired behavios of pandoc.utils.stringify, such as quotes and -- backslashes conversions. - -- @param content pandoc.MetaValue: Pandoc content value + -- @param content pandoc.MetaValue: Pandoc content value -- @return string: Pandoc stringified value function pandoc.utils.rawstringify(content) return pandoc.utils.stringify(content:walk { @@ -263,7 +295,7 @@ if pandoc ~= nil then RawInline = function(rawinline) return pandoc.Str(rawinline.text:gsub("\\n", "\n"):gsub("\\t", "\t")) end, - SoftBreak = function(softbreak) + SoftBreak = function(_) return pandoc.Str("\n") end, Inline = function(inline) return pandoc.utils.stringify(inline) end, @@ -295,3 +327,5 @@ if pandoc ~= nil then end end + +return dog