/** * @file ArgumentParser.h * @author apio (cloudapio.eu) * @brief Command-line argument parser. * * @copyright Copyright (c) 2023, the Luna authors. * */ #pragma once #include #include namespace os { /** * @brief A simple command-line argument parser. */ class ArgumentParser { public: /** * @brief Construct a new ArgumentParser object. */ ArgumentParser(); /** * @brief Add a description for this command-line utility (shown in the help text). * * @param description The description to use. */ void add_description(StringView description); /** * @brief Register a new positional argument. * * @param out The variable where the argument's value will be stored. * @param name The positional argument's name. * @param fallback The value to use if the user does not provide one. * @return Result Whether the operation succeeded. */ Result add_positional_argument(StringView& out, StringView name, StringView fallback); /** * @brief Register a new positional argument. * * @param out The variable where the argument's value will be stored. * @param name The positional argument's name. * @param required Whether the user must enter a value for this argument. If this is false and the user does not * enter a value, out will be set to an empty string. * @return Result Whether the operation succeeded. */ Result add_positional_argument(StringView& out, StringView name, bool required); /** * @brief Register a new switch argument. * * @param out This variable will be set to true if the user provides the switch argument on the command * line. * @param short_flag The short flag to use for this argument, excluding the '-' prefix: an option '-c' would be * passed as 'c'. Can only be a single ASCII character. If you do not wish this argument to have a short flag, * use the space character: ' '. * @param long_flag The long flag to use for this argument, excluding the '--' prefix: an option '--example' * would be passed as 'example'. Can be a string of any length. If you do not wish this argument to have a long * flag, use an empty string: "". * @param help The help text to show for this argument (optional). * @return Result Whether the operation succeeded. */ Result add_switch_argument(bool& out, char short_flag, StringView long_flag, StringView help = {}); /** * @brief Register a new value argument. * * @param out The variable where the argument's value will be stored. * @param short_flag The short flag to use for this argument, excluding the '-' prefix: an option '-c' would be * passed as 'c'. Can only be a single ASCII character. If you do not wish this argument to have a short flag, * use the space character: ' '. * @param long_flag The long flag to use for this argument, excluding the '--' prefix: an option '--example' * would be passed as 'example'. Can be a string of any length. If you do not wish this argument to have a long * flag, use an empty string: "". * @param help The help text to show for this argument (optional). * @return Result Whether the operation succeeded. */ Result add_value_argument(StringView& out, char short_flag, StringView long_flag, StringView help = {}); /** * @brief Register a positional argument vector (can be only done once). * * @param out The vector of StringViews to store arguments in. * @param name The name to give to the argument vector (in the help text). * @param fallback The value to put in the vector if no values are provided. * @param allow_no_more_flags If set, after starting to append values into the vector, no more flags (switch and * value arguments) will be parsed, and will instead be treated as regular positional arguments, as if '--' had * been specified on the command line. */ Result set_vector_argument(Vector& out, StringView name, StringView fallback, bool allow_no_more_flags = false); /** * @brief Register a positional argument vector (can be only done once). * * @param out The vector of StringViews to store arguments in. * @param name The name to give to the argument vector (in the help text). * @param required Whether the user must enter at least one value for this argument. If this is false and the * user does not enter a value, out will be cleared. * @param allow_no_more_flags If set, after starting to append values into the vector, no more flags (switch and * value arguments) will be parsed, and will instead be treated as regular positional arguments, as if '--' had * been specified on the command line. */ Result set_vector_argument(Vector& out, StringView name, bool required, bool allow_no_more_flags = false); /** * @brief Parse the given command-line using this ArgumentParser's registered arguments. * * @param argc The argc value passed to main() or luna_main(). * @param argv The argv value passed to main() or luna_main(). * @return Result Whether the operation succeeded. */ Result parse(int argc, char* const* argv); /** * @brief A program's copyright and version information. */ struct ProgramInfo { StringView name; // The program's name. StringView version; // The program's version/release. StringView copyright; // The program's copyright statement. StringView license; // The program's licensing information. StringView authors; // The program's authors (optional). StringView package; // The package the program is a part of (optional). }; /** * @brief Set this program's copyright and version information (shown in the version text). * * @param info The program information to use. */ void add_program_info(ProgramInfo info); /** * @brief For programs that are part of the Luna source tree, set the version information using Luna's own * version and copyright info and the program name. * * @param name The program name to show in the version information. */ void add_system_program_info(StringView name); /** * @brief Show a short message describing how to find usage information (usually " --help"). * * The actual message shown is: * "Try running ' --help' for more information." if --help has not been overridden. * "Try running ' -h' for more information." if -h has not been overridden. * If both have been overridden, no output is shown. * * Then, the program exits with a non-zero exit code. * This function is designed to be used when the program detects an invalid argument value after parse() has run * successfully. * * @param program_name The program name to show (usually argv[0]). */ void short_usage(StringView program_name); private: struct PositionalArgument { StringView* out; StringView name; bool required; StringView fallback; }; struct SwitchArgument { bool* out; char short_flag; StringView long_flag; StringView help; }; struct ValueArgument { StringView* out; char short_flag; StringView long_flag; StringView help; }; Result usage(StringView program_name); void version(); Vector m_positional_args; Vector m_switch_args; Vector m_value_args; Vector* m_vector_argument { nullptr }; bool m_allow_no_more_flags_after_vector_argument_start { false }; ProgramInfo m_program_info; StringView m_description = {}; bool m_add_short_help_flag { false }; bool m_add_long_help_flag { false }; bool m_add_short_version_flag { false }; bool m_add_long_version_flag { false }; }; }