Added CLI parser library

This library can easily process flags, arguments, and flags with
arguments.
This commit is contained in:
2025-09-24 21:29:34 +03:00
parent ad0f2197a3
commit 99725b43ba
+120
View File
@@ -0,0 +1,120 @@
local cliParse = {}
local cliParseConfig
function cliParse.config(config)
-- Check if config is valid
checkArg(1, config, "table")
for flagName, argCount in pairs(config) do
if type(flagName) ~= "string" then
error("Flag name " .. tostring(flagName) .. " must be a string")
end
if type(argCount) == "table" then -- Min and max arg count specified
if type(argCount[1]) ~= "number" then
error("Min args for flag " .. flagName .. " must be a number")
end
if argCount[1] < 0 then
error("Min args for flag " .. flagName .. " must not be lower than 0")
end
if type(argCount[2]) ~= "number" then
error("Max args for flag " .. flagName .. " must be a number")
end
if argCount[2] < 0 then
error("Max args for flag " .. flagName .. " must not be lower than 0")
end
if argCount[2] < argCount[1] then
error("Max args for flag " .. flagName .. " must be more than or equal to min args")
end
elseif type(argCount) == "number" then -- Required arg count specified
if argCount < 0 then
error("Required args for flag " .. flagName .. " must not be lower than 0")
end
else
error("Arg count for flag " .. flagName .. " must be either a table of 2 numbers or a number")
end
-- Config is all good, set it
cliParseConfig = config
end
end
function cliParse.parse(...)
local args = { ... }
local returnTable = { ["flags"] = {}, ["args"] = {} } -- This will be filled out and returned in the end
for i = 1, #args do -- This is used instead of pairs() so that the code can skip ahead when it finds arguments instead of flags by just incrementing i
local flagList = {}
if args[i]:sub(1, 2) == "--" then -- Long flag
flagList = { args[i]:sub(3) }
elseif args[i]:sub(1, 1) == "-" then -- Short flag(s)
for i2 = 2, #args[i] do -- i is 2 to account for the - character at the start
table.insert(flagList, args[i]:sub(i2, i2))
end
end
for flagIndex, flag in ipairs(flagList) do -- Yes, this has to be in the argument loop for the skipahead to work.
local flagConfig = cliParseConfig[flag]
if flagConfig then -- This is a real flag
returnTable.flags[flag] = {}
if type(flagConfig) == "table" then
if flagIndex ~= #flagList then -- This flag is in a chain and it's not the last one
if flagConfig[1] ~= 0 then
return false, "Flag " .. flag .. " expects at least " .. flagConfig[1] .. " arguments, got 0"
end
else
for i2 = 1, flagConfig[1] do -- Iterate through the items AFTER the flags to find the minimum arguments
if args[i + i2]:sub(1, 1) ~= "-" then -- This checks for both long and short flags
table.insert(returnTable.flags[flag], args[i + i2]) -- Insert the argument found into the return table
else
return false, "Flag " .. flag .. " expects at least " .. flagConfig[1] .. " arguments, got " .. i2
end
end
i = i + flagConfig[1]
local i2IncrementAmount -- See line 71 and 76
for i2 = 1, flagConfig[2] - flagConfig[1] do -- Now search for the max args
if args[i + i2]:sub(1, 1) ~= "-" then -- This checks for both long and short flags
table.insert(returnTable.flags[flag], args[i + i2]) -- Insert the argument found into the return table
else
i2IncrementAmount = i2 - 1
-- Since the current argument is NOT a valid one, decrement by 1 to return to the last valid one (this is important when adding i2 to i)
break
end
end
i = i + (i2IncrementAmount or flagConfig[2])
end
else -- Flag required args are a single number
if flagIndex ~= #flagList then -- This flag is in a chain and it's not the last one
if flagConfig ~= 0 then
return false, "Flag " .. flag .. " expects " .. flagConfig .. " arguments, got 0"
end
else
for i2 = 1, flagConfig do -- Now search for the max args
if args[i + i2]:sub(1, 1) ~= "-" then -- This checks for both long and short flags
table.insert(returnTable.flags[flag], args[i + i2]) -- Insert the argument found into the return table
else
return false, "Flag " .. flag .. " expects " .. flagConfig[1] .. " arguments, got " .. i2
end
end
i = i + flagConfig
end
end
else
return false, "Unexpected flag: " .. flag
end
end
end
for _, arg in pairs(args) do
local foundArg = false
for _, flag in pairs(returnTable.flags) do -- A loop in a loop... Peak efficiency
if table.find(flag, arg) or arg:sub(1, 1) == "-" then -- AAAND ANOTHER LOOP?!
foundArg = true
break
end
end
if not foundArg then
table.insert(returnTable.args, arg)
end
end
return returnTable
end
return cliParse