view C++/modules/OptionParser/OptionParser.h @ 334:0b576ee64d45

* Create brand new hierarchy * Rename DynLib to Dynlib * Remove some warnings
author David Demelier <markand@malikania.fr>
date Sun, 08 Mar 2015 14:26:33 +0100
parents C++/OptionParser.h@99e83685d4da
children d5ec1174b707
line wrap: on
line source

/*
 * OptionParser.h -- command line option parser
 *
 * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#ifndef _OPTION_PARSER_H_
#define _OPTION_PARSER_H_

/**
 * @file OptionParser.h
 * @brief Command line option parser
 */

#include <initializer_list>
#include <string>
#include <vector>

/**
 * @class Option
 * @brief Option definition
 */
class Option {
public:
	enum Flags {
		NoArg	= (1 << 0),
	};

private:
	std::string m_key;
	std::string m_full;
	int m_flags;

public:
	/**
	 * Construct an option. By default, an option requires an argument
	 * unless flags is set to NoArg.
	 *
	 * You <strong>must</strong> not prepend dashes to the option names.
	 *
	 * You don't need to set both short and long names, but you need at
	 * least one.
	 *
	 * @param key the short name (e.g v)
	 * @param full the long name (e.g verbose)
	 * @param flags the optional flags
	 * @see Flags
	 */
	inline Option(std::string key, std::string full, int flags = 0)
		: m_key(std::move(key))
		, m_full(std::move(full))
		, m_flags(flags)
	{
	}

	/**
	 * Get the short name (e.g v)
	 *
	 * @return the short name
	 */
	inline const std::string &key() const noexcept
	{
		return m_key;
	}

	/**
	 * Get the long name (e.g verbose)
	 *
	 * @return the long name
	 */
	inline const std::string &full() const noexcept
	{
		return m_full;
	}

	/**
	 * Get the flags.
	 *
	 * @return the flags
	 * @see Flags
	 */
	inline int flags() const noexcept
	{
		return m_flags;
	}
};

/**
 * @class OptionValue
 * @brief Result of an option parse
 */
class OptionValue {
private:
	std::string m_key;
	std::string m_full;
	std::string m_value;

public:
	/**
	 * Construct an option value
	 *
	 * @param option the option
	 * @param value the value
	 */
	inline OptionValue(const Option &option, std::string value)
		: m_key(option.key())
		, m_full(option.full())
		, m_value(std::move(value))
	{
	}

	/**
	 * Get the value (if the option requires an argument).
	 *
	 * @return the value
	 */
	inline const std::string &value() const noexcept
	{
		return m_value;
	}

	friend bool operator==(const OptionValue &o1, const std::string &name);
};

/**
 * Test the option value with the specified option name.
 *
 * You can use both the short option or the long option name depending
 * on what you have registered to the OptionParser class.
 *
 * @param o the option
 * @param name the short or the full name
 * @return true if matches
 */
inline bool operator==(const OptionValue &o, const std::string &name)
{
	return o.m_key == name || o.m_full == name;
}

/**
 * @class OptionPack
 * @brief Object containing results of a parse
 *
 * Because parsing bad options does not throw exceptions, this class is
 * convertible to bool and has the error contained.
 *
 * It also have the number of arguments parsed so you can cut your options
 * depending on the full command line.
 *
 * Example:
 *	-y install -d foo
 *	-y remove -f
 *
 * In that case, you can do two parsing, it will stops (unless Unstrict is set)
 * until install or remove.
 */
class OptionPack : public std::vector<OptionValue> {
private:
	friend class OptionParser;

	std::string m_error{"No error"};
	int m_argsParsed{0};

public:
	/**
	 * Get the error.
	 *
	 * @return the error
	 */
	inline const std::string &error() const noexcept
	{
		return m_error;
	}

	/**
	 * Get the number of arguments parsed <strong>not the number of
	 * options</strong>.
	 *
	 * @return the number of arguments parsed
	 */
	inline int parsed() const noexcept
	{
		return m_argsParsed;
	}

	/**
	 * Convert to true on success.
	 *
	 * @return true on success
	 */
	inline operator bool() const noexcept
	{
		return m_error == "No error";
	}
};

/**
 * @class OptionParser
 * @brief Base class for parsing command line options
 *
 * The option parser is a replacement for getopt(3) which is reentrant
 * and does not use globals.
 */
class OptionParser {
public:
	using Map = std::vector<Option>;
	using Args = std::vector<std::string>;

	enum Flags {
		Unstrict =	(1 << 0)
	};

private:
	Map m_options;

	const Option &get(const std::string &arg) const;
	std::string key(const std::string &arg) const;
	bool isDefined(const std::string &arg) const;
	bool isToggle(const std::string &arg) const;
	bool isShortCompacted(const std::string &arg) const;
	bool isShort(const std::string &arg) const;
	bool isLong(const std::string &arg) const;
	bool isOption(const std::string &arg) const;
	void readShort(OptionPack &pack, Args::const_iterator &it, Args::const_iterator end) const;
	void readFull(OptionPack &pack, Args::const_iterator &it, Args::const_iterator end) const;
	OptionPack parse(Args::const_iterator it, Args::const_iterator end, int flags) const;

public:
	/**
	 * Construct an option parser from an initializer_list of options.
	 *
	 * @param options the list of options
	 */
	OptionParser(std::initializer_list<Option> options);

	/**
	 * Construct an option parser from a vector of options.
	 *
	 * @param options the options
	 */
	OptionParser(std::vector<Option> options);

	/**
	 * Parse the arguments from main arguments.
	 *
	 * @param argc the number of arguments
	 * @param argv the arguments
	 * @param flags the optional flags
	 * @return the packed result
	 */
	OptionPack parse(int argc, char **argv, int flags = 0) const;

	/**
	 * Parse the arguments from a vector.
	 *
	 * @param args the arguments
	 * @param flags the optional flags
	 * @return the packed result
	 */
	OptionPack parse(const std::vector<std::string> &args, int flags = 0) const;
};

#endif // !_OPTION_PARSER_H_