tools: nfdc command line parser

refs #3749

Change-Id: Ief301152212a6d501f0396b2c9834e860ddaf6c5
diff --git a/tools/nfdc/command-definition.hpp b/tools/nfdc/command-definition.hpp
new file mode 100644
index 0000000..136f5c4
--- /dev/null
+++ b/tools/nfdc/command-definition.hpp
@@ -0,0 +1,211 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2016,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NFD_TOOLS_NFDC_COMMAND_DEFINITION_HPP
+#define NFD_TOOLS_NFDC_COMMAND_DEFINITION_HPP
+
+#include "command-arguments.hpp"
+
+namespace nfd {
+namespace tools {
+namespace nfdc {
+
+/** \brief indicates argument value type
+ */
+enum class ArgValueType {
+  /** \brief boolean argument without value
+   *
+   *  The argument appears in CommandArguments as bool value 'true'.
+   *  It must not be declared as positional.
+   */
+  NONE,
+
+  /** \brief any arguments
+   *
+   *  The argument appears in CommandArguments as std::vector<std::string>.
+   *  It must be declared as positional, and will consume all subsequent tokens.
+   */
+  ANY,
+
+  /** \brief non-negative integer
+   *
+   *  The argument appears in CommandArguments as uint64_t.
+   *  Acceptable input range is [0, std::numeric_limits<int64_t>::max()].
+   */
+  UNSIGNED,
+
+  /** \brief arbitrary string
+   *
+   *  The argument appears in CommandArguments as std::string.
+   */
+  STRING,
+
+  /** \brief report format 'xml' or 'text'
+   *
+   *  The argument appears in CommandArguments as nfd::tools::nfdc::ReportFormat.
+   */
+  REPORT_FORMAT,
+
+  /** \brief Name prefix
+   *
+   *  The argument appears in CommandArguments as ndn::Name.
+   */
+  NAME,
+
+  /** \brief FaceUri
+   *
+   *  The argument appears in CommandArguments as ndn::util::FaceUri.
+   */
+  FACE_URI,
+
+  /** \brief FaceId or FaceUri
+   *
+   *  The argument appears in CommandArguments as either uint64_t or ndn::util::FaceUri.
+   */
+  FACE_ID_OR_URI,
+
+  /** \brief face persistency 'persistent' or 'permanent'
+   *
+   *  The argument appears in CommandArguments as ndn::nfd::FacePersistency.
+   */
+  FACE_PERSISTENCY
+};
+
+std::ostream&
+operator<<(std::ostream& os, ArgValueType vt);
+
+/** \brief indicates whether an argument is required
+ */
+enum class Required {
+  NO = false, ///< argument is optional
+  YES = true  ///< argument is required
+};
+
+/** \brief indicates whether an argument can be specified as positional
+ */
+enum class Positional {
+  NO = false, ///< argument must be named
+  YES = true  ///< argument can be specified as positional
+};
+
+/** \brief declares semantics of a command
+ */
+class CommandDefinition
+{
+public:
+  class Error : public std::invalid_argument
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : std::invalid_argument(what)
+    {
+    }
+  };
+
+  CommandDefinition(const std::string& noun, const std::string& verb);
+
+  ~CommandDefinition();
+
+  const std::string
+  getNoun() const
+  {
+    return m_noun;
+  }
+
+  const std::string
+  getVerb() const
+  {
+    return m_verb;
+  }
+
+public: // help
+  /** \return one-line synopsis
+   */
+  const std::string&
+  getSynopsis() const
+  {
+    return m_synopsis;
+  }
+
+  /** \brief set one-line synopsis
+   */
+  CommandDefinition&
+  setSynopsis(const std::string& synopsis)
+  {
+    m_synopsis = synopsis;
+    return *this;
+  }
+
+public: // arguments
+  /** \brief declare an argument
+   *  \param name argument name, must be unique
+   *  \param valueType argument value type
+   *  \param isRequired whether the argument is required
+   *  \param allowPositional whether the argument value can be specified as positional
+   *  \param metavar displayed argument value placeholder
+   */
+  CommandDefinition&
+  addArg(const std::string& name, ArgValueType valueType,
+         Required isRequired = Required::NO,
+         Positional allowPositional = Positional::NO,
+         const std::string& metavar = "");
+
+  /** \brief parse a command line
+   *  \param tokens command line tokens
+   *  \param start command line start position, after noun and verb
+   *  \throw Error command line is invalid
+   */
+  CommandArguments
+  parse(const std::vector<std::string>& tokens, size_t start = 0) const;
+
+private:
+  boost::any
+  parseValue(ArgValueType valueType, const std::string& token) const;
+
+private:
+  std::string m_noun;
+  std::string m_verb;
+
+  std::string m_synopsis;
+
+  struct Arg
+  {
+    std::string name;
+    ArgValueType valueType;
+    bool isRequired;
+    std::string metavar;
+  };
+  typedef std::map<std::string, Arg> ArgMap;
+  ArgMap m_args;
+  std::set<std::string> m_requiredArgs;
+  std::vector<std::string> m_positionalArgs;
+};
+
+} // namespace nfdc
+} // namespace tools
+} // namespace nfd
+
+#endif // NFD_TOOLS_NFDC_COMMAND_DEFINITION_HPP