Skip to content

Instantly share code, notes, and snippets.

@rfl890
Created November 30, 2023 22:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rfl890/26e369bb8e5ffdf774dea37f1563f28e to your computer and use it in GitHub Desktop.
Save rfl890/26e369bb8e5ffdf774dea37f1563f28e to your computer and use it in GitHub Desktop.
Lua color library
local lib_color = {}
-- Constants
lib_color.color_type = { TYPE_8 = 39710315, TYPE_256 = 34942623, TYPE_RGB = 29505187 }
local FOREGROUND = 1
local BACKGROUND = 2
local ESCAPE = "\x1b["
local RESET = "\x1b[0m"
lib_color.modifiers = {
BOLD = "1",
DIM = "2",
ITALIC = "3",
UNDERLINE = "4",
BLINKING = "5",
INVERSE = "7",
INVISIBLE = "8",
STRIKETHROUGH = "9"
}
lib_color.colors = {
BLACK = { "30", "40" },
RED = { "31", "41" },
GREEN = { "32", "42" },
YELLOW = { "33", "43" },
BLUE = { "34", "44" },
MAGENTA = { "35", "45" },
CYAN = { "36", "46" },
WHITE = { "37", "47" },
DEFAULT = { "39", "39" }
}
-- Utility functions
local function assert_conditionally(condition, value, error) if condition then assert(value, error) end end
local function checkIfExistsInTable(item, table, checkIdx, idx)
checkIdx = checkIdx or false
for _, member in pairs(table) do
if checkIdx then
if item == member[idx] then
return true
end
else
if item == member then
return true
end
end
end
return false
end
local function checkIndexesInTable(item, table)
for _, member in ipairs(item) do
if not checkIfExistsInTable(member, table) then return false end
end
return true
end
local function validateRGB(rgb)
if type(rgb) ~= "table" then return false end
if #rgb < 3 then return false end
for i = 1, 3 do
local n = tonumber(rgb[i])
if (n == nil) or (not (n >= 0 and n <= 255)) then return false end
end
return true
end
-- Library functions
lib_color.format = function(color_type, modifiers, color, background, text)
-- Validate color_type and modifiers
assert(checkIfExistsInTable(color_type, lib_color.color_type), "Argument 'color_type' must be one of: TYPE_8, TYPE_256 or TYPE_RGB.")
assert(type(modifiers) == "table", "Argument 'modifiers' must be a table.")
assert(checkIndexesInTable(modifiers, lib_color.modifiers), "Argument 'modifiers' contains an invalid modifier.")
-- Validate color and background
assert_conditionally(color_type == lib_color.color_type.TYPE_RGB, validateRGB(color), "Argument 'color' must be a table with 3 numbers >= 0 and <= 255 when using RGB.")
assert_conditionally((color_type == lib_color.color_type.TYPE_RGB) and background, validateRGB(background), "Argument 'background' must be a table with 3 numbers >= 0 and <= 255 when using RGB.")
assert_conditionally(color_type == lib_color.color_type.TYPE_8, checkIfExistsInTable((type(color) == "table" and color or {})[FOREGROUND], lib_color.colors, true, FOREGROUND), "Argument 'color' must be a valid color when using 8 colors.")
assert_conditionally((color_type == lib_color.color_type.TYPE_8) and background, checkIfExistsInTable((type(background) == "table" and background or {})[BACKGROUND], lib_color.colors, true, BACKGROUND), "Argument 'background' must be a valid color when using 8 colors.")
assert_conditionally(color_type == lib_color.color_type.TYPE_256, tonumber(color) and (tonumber(color) >= 0 and tonumber(color) <= 255), "Argument 'color' must be a number >= 0 and <= 255 when using 256 colors.")
assert_conditionally((color_type == lib_color.color_type.TYPE_256) and background, tonumber(background) and (tonumber(background) >= 0 and tonumber(background) <= 255), "Argument 'background' must be a number >= 0 and <= 255 when using 256 colors.")
local fmttdString = ESCAPE
local modifiersAlreadySeen = {}
if color_type == lib_color.color_type.TYPE_8 then
for _, modifier in pairs(modifiers) do
if not checkIfExistsInTable(modifier, modifiersAlreadySeen) then
fmttdString = fmttdString .. modifier .. ";"
table.insert(modifiersAlreadySeen, modifier)
end
end
if background then
fmttdString = string.format("%s%s;%sm%s%s", fmttdString, color[FOREGROUND], background[BACKGROUND], text, RESET)
else
fmttdString = string.format("%s%sm%s%s", fmttdString, color[FOREGROUND], text, RESET)
end
elseif color_type == lib_color.color_type.TYPE_256 then
for _, modifier in pairs(modifiers) do
if not checkIfExistsInTable(modifier, modifiersAlreadySeen) then
fmttdString = fmttdString .. modifier .. ";"
table.insert(modifiersAlreadySeen, modifier)
end
end
if background then
fmttdString = string.format("%s38;5;%s;48;5;%sm%s%s", fmttdString, tostring(color), tostring(background), text, RESET)
else
fmttdString = string.format("%s38;5;%sm%s%s", fmttdString, tostring(color), text, RESET)
end
else
for _, modifier in pairs(modifiers) do
if not checkIfExistsInTable(modifier, modifiersAlreadySeen) then
fmttdString = fmttdString .. modifier .. ";"
table.insert(modifiersAlreadySeen, modifier)
end
end
if background then
fmttdString = string.format("%s38;2;%s;48;2;%sm%s%s", fmttdString, table.concat(color, ";"), table.concat(background, ";"), text, RESET)
else
fmttdString = string.format("%s38;2;%sm%s%s", fmttdString, table.concat(color, ";"), text, RESET)
end
end
return fmttdString
end
return lib_color
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment