local wordwrap = require('cliargs.utils.wordwrap') local filter = require('cliargs.utils.filter') local K = require('cliargs.constants') local MAX_COLS = 72 local _ local function create_printer(get_parser_state) local printer = {} function printer.print(msg) return _G.print(msg) end local function get_max_label_length() local maxsz = 0 local state = get_parser_state() local optargument = filter(state.options, 'type', K.TYPE_SPLAT)[1] local commands = filter(state.options, 'type', K.TYPE_COMMAND) for _, entry in ipairs(commands) do if #entry.__key__ > maxsz then maxsz = #entry.__key__ end end for _,table_name in ipairs({"options"}) do for _, entry in ipairs(state[table_name]) do local key = entry.label or entry.key or entry.__key__ if #key > maxsz then maxsz = #key end end end if optargument and #optargument.key > maxsz then maxsz = #optargument.key end return maxsz end -- Generate the USAGE heading message. function printer.generate_usage() local state = get_parser_state() local msg = "Usage:" local required = filter(state.options, 'type', K.TYPE_ARGUMENT) local optional = filter(state.options, 'type', K.TYPE_OPTION) local optargument = filter(state.options, 'type', K.TYPE_SPLAT)[1] if #state.name > 0 then msg = msg .. ' ' .. tostring(state.name) end if #optional > 0 then msg = msg .. " [OPTIONS]" end if #required > 0 or optargument then msg = msg .. " [--]" end if #required > 0 then for _,entry in ipairs(required) do msg = msg .. " " .. entry.key end end if optargument then if optargument.maxcount == 1 then msg = msg .. " [" .. optargument.key .. "]" elseif optargument.maxcount == 2 then msg = msg .. " [" .. optargument.key .. "-1 [" .. optargument.key .. "-2]]" elseif optargument.maxcount > 2 then msg = msg .. " [" .. optargument.key .. "-1 [" .. optargument.key .. "-2 [...]]]" end end return msg end function printer.generate_help() local msg = '' local state = get_parser_state() local col1 = state.colsz[1] local col2 = state.colsz[2] local required = filter(state.options, 'type', K.TYPE_ARGUMENT) local optional = filter(state.options, 'type', K.TYPE_OPTION) local commands = filter(state.options, 'type', K.TYPE_COMMAND) local optargument = filter(state.options, 'type', K.TYPE_SPLAT)[1] local function append(label, desc) label = " " .. label .. string.rep(" ", col1 - (#label + 2)) desc = wordwrap(desc, col2) -- word-wrap desc = desc:gsub("\n", "\n" .. string.rep(" ", col1)) -- add padding msg = msg .. label .. desc .. "\n" end if col1 == 0 then col1 = get_max_label_length(state) end -- add margins col1 = col1 + 3 if col2 == 0 then col2 = MAX_COLS - col1 end if col2 < 10 then col2 = 10 end if #commands > 0 then msg = msg .. "\nCOMMANDS: \n" for _, entry in ipairs(commands) do append(entry.__key__, entry.description or '') end end if required[1] or optargument then msg = msg .. "\nARGUMENTS: \n" for _,entry in ipairs(required) do append(entry.key, entry.desc .. " (required)") end end if optargument then local optarg_desc = ' ' .. optargument.desc local default_value = optargument.maxcount > 1 and optargument.default[1] or optargument.default if #optargument.default > 0 then optarg_desc = optarg_desc .. " (optional, default: " .. tostring(default_value[1]) .. ")" else optarg_desc = optarg_desc .. " (optional)" end append(optargument.key, optarg_desc) end if #optional > 0 then msg = msg .. "\nOPTIONS: \n" for _,entry in ipairs(optional) do local desc = entry.desc if not entry.flag and entry.default and #tostring(entry.default) > 0 then local readable_default = type(entry.default) == "table" and "[]" or tostring(entry.default) desc = desc .. " (default: " .. readable_default .. ")" elseif entry.flag and entry.negatable then local readable_default = entry.default and 'on' or 'off' desc = desc .. " (default: " .. readable_default .. ")" end append(entry.label, desc) end end return msg end function printer.dump_internal_state(values) local state = get_parser_state() local required = filter(state.options, 'type', K.TYPE_ARGUMENT) local optional = filter(state.options, 'type', K.TYPE_OPTION) local optargument = filter(state.options, 'type', K.TYPE_SPLAT)[1] local maxlabel = get_max_label_length() local msg = '' local function print(fragment) msg = msg .. fragment .. '\n' end print("\n======= Provided command line =============") print("\nNumber of arguments: ", #arg) for i,v in ipairs(arg) do -- use gloabl 'arg' not the modified local 'args' print(string.format("%3i = '%s'", i, v)) end print("\n======= Parsed command line ===============") if #required > 0 then print("\nArguments:") end for _, entry in ipairs(required) do print( " " .. entry.key .. string.rep(" ", maxlabel + 2 - #entry.key) .. " => '" .. tostring(values[entry]) .. "'" ) end if optargument then print( "\nOptional arguments:" .. optargument.key .. "; allowed are " .. tostring(optargument.maxcount) .. " arguments" ) if optargument.maxcount == 1 then print( " " .. optargument.key .. string.rep(" ", maxlabel + 2 - #optargument.key) .. " => '" .. optargument.key .. "'" ) else for i = 1, optargument.maxcount do if values[optargument] and values[optargument][i] then print( " " .. tostring(i) .. string.rep(" ", maxlabel + 2 - #tostring(i)) .. " => '" .. tostring(values[optargument][i]) .. "'" ) end end end end if #optional > 0 then print("\nOptional parameters:") end local doubles = {} for _, entry in pairs(optional) do if not doubles[entry] then local value = values[entry] if type(value) == "string" then value = "'"..value.."'" else value = tostring(value) .." (" .. type(value) .. ")" end print(" " .. entry.label .. string.rep(" ", maxlabel + 2 - #entry.label) .. " => " .. value) doubles[entry] = entry end end print("\n===========================================\n\n") return msg end function printer.generate_help_and_usage() local msg = '' msg = msg .. printer.generate_usage() .. '\n' msg = msg .. printer.generate_help() return msg end return printer end return create_printer