diff --git a/lib/cliparse.lua b/lib/cliparse.lua new file mode 100644 index 0000000..44c5a49 --- /dev/null +++ b/lib/cliparse.lua @@ -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