tools: nfdc command line parser
refs #3749
Change-Id: Ief301152212a6d501f0396b2c9834e860ddaf6c5
diff --git a/tests/tools/nfdc/command-definition.t.cpp b/tests/tools/nfdc/command-definition.t.cpp
new file mode 100644
index 0000000..bbefe81
--- /dev/null
+++ b/tests/tools/nfdc/command-definition.t.cpp
@@ -0,0 +1,326 @@
+/* -*- 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/>.
+ */
+
+#include "nfdc/command-definition.hpp"
+#include "nfdc/status-report.hpp"
+#include <ndn-cxx/encoding/nfd-constants.hpp>
+#include <ndn-cxx/util/face-uri.hpp>
+
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace tools {
+namespace nfdc {
+namespace tests {
+
+using namespace nfd::tests;
+
+using ndn::util::FaceUri;
+using ndn::nfd::FacePersistency;
+
+BOOST_AUTO_TEST_SUITE(Nfdc)
+BOOST_FIXTURE_TEST_SUITE(TestCommandDefinition, BaseFixture)
+
+BOOST_AUTO_TEST_SUITE(Arguments)
+
+BOOST_AUTO_TEST_CASE(NoArg)
+{
+ CommandDefinition cs("noun", "verb");
+
+ CommandArguments ca;
+
+ ca = cs.parse(std::vector<std::string>{});
+ BOOST_CHECK_EQUAL(ca.size(), 0);
+
+ BOOST_CHECK_THROW(cs.parse(std::vector<std::string>{"x"}), CommandDefinition::Error);
+ BOOST_CHECK_THROW(cs.parse(std::vector<std::string>{"x", "y"}), CommandDefinition::Error);
+}
+
+BOOST_AUTO_TEST_CASE(NamedArgs)
+{
+ CommandDefinition cs("noun", "verb");
+ cs.addArg("a", ArgValueType::UNSIGNED, Required::NO, Positional::NO, "int")
+ .addArg("b", ArgValueType::NAME, Required::NO, Positional::NO, "name");
+
+ CommandArguments ca;
+
+ ca = cs.parse(std::vector<std::string>{});
+ BOOST_CHECK_EQUAL(ca.size(), 0);
+
+ ca = cs.parse(std::vector<std::string>{"a", "1"});
+ BOOST_CHECK_EQUAL(ca.size(), 1);
+ BOOST_CHECK_EQUAL(ca.get<uint64_t>("a"), 1);
+
+ ca = cs.parse(std::vector<std::string>{"a", "1", "b", "/n"});
+ BOOST_CHECK_EQUAL(ca.size(), 2);
+ BOOST_CHECK_EQUAL(ca.get<uint64_t>("a"), 1);
+ BOOST_CHECK_EQUAL(ca.get<Name>("b"), "/n");
+
+ ca = cs.parse(std::vector<std::string>{"b", "/n", "a", "1"});
+ BOOST_CHECK_EQUAL(ca.size(), 2);
+ BOOST_CHECK_EQUAL(ca.get<uint64_t>("a"), 1);
+ BOOST_CHECK_EQUAL(ca.get<Name>("b"), "/n");
+
+ BOOST_CHECK_THROW(cs.parse(std::vector<std::string>{"1"}), CommandDefinition::Error);
+ BOOST_CHECK_THROW(cs.parse(std::vector<std::string>{"c", "1"}), CommandDefinition::Error);
+}
+
+BOOST_AUTO_TEST_CASE(PositionalArgs)
+{
+ CommandDefinition cs("face", "create");
+ cs.addArg("remote", ArgValueType::FACE_URI, Required::YES, Positional::YES)
+ .addArg("persistency", ArgValueType::FACE_PERSISTENCY, Required::NO, Positional::YES);
+
+ CommandArguments ca;
+
+ ca = cs.parse(std::vector<std::string>{"udp4://router.example.com", "persistent"});
+ BOOST_CHECK_EQUAL(ca.size(), 2);
+ BOOST_CHECK_EQUAL(ca.get<FaceUri>("remote"),
+ FaceUri("udp4://router.example.com"));
+ BOOST_CHECK_EQUAL(ca.get<FacePersistency>("persistency"),
+ FacePersistency::FACE_PERSISTENCY_PERSISTENT);
+
+ ca = cs.parse(std::vector<std::string>{"udp4://router.example.com"});
+ BOOST_CHECK_EQUAL(ca.size(), 1);
+ BOOST_CHECK_EQUAL(ca.get<FaceUri>("remote"),
+ FaceUri("udp4://router.example.com"));
+
+ ca = cs.parse(std::vector<std::string>{"remote", "udp4://router.example.com"});
+ BOOST_CHECK_EQUAL(ca.size(), 1);
+ BOOST_CHECK_EQUAL(ca.get<FaceUri>("remote"),
+ FaceUri("udp4://router.example.com"));
+
+ ca = cs.parse(std::vector<std::string>{
+ "udp4://router.example.com", "persistency", "persistent"});
+ BOOST_CHECK_EQUAL(ca.size(), 2);
+ BOOST_CHECK_EQUAL(ca.get<FaceUri>("remote"),
+ FaceUri("udp4://router.example.com"));
+ BOOST_CHECK_EQUAL(ca.get<FacePersistency>("persistency"),
+ FacePersistency::FACE_PERSISTENCY_PERSISTENT);
+
+ ca = cs.parse(std::vector<std::string>{
+ "remote", "udp4://router.example.com", "persistency", "persistent"});
+ BOOST_CHECK_EQUAL(ca.size(), 2);
+ BOOST_CHECK_EQUAL(ca.get<FaceUri>("remote"),
+ FaceUri("udp4://router.example.com"));
+ BOOST_CHECK_EQUAL(ca.get<FacePersistency>("persistency"),
+ FacePersistency::FACE_PERSISTENCY_PERSISTENT);
+
+ ca = cs.parse(std::vector<std::string>{
+ "persistency", "persistent", "remote", "udp4://router.example.com"});
+ BOOST_CHECK_EQUAL(ca.size(), 2);
+ BOOST_CHECK_EQUAL(ca.get<FaceUri>("remote"),
+ FaceUri("udp4://router.example.com"));
+ BOOST_CHECK_EQUAL(ca.get<FacePersistency>("persistency"),
+ FacePersistency::FACE_PERSISTENCY_PERSISTENT);
+
+ BOOST_CHECK_THROW(cs.parse(std::vector<std::string>{
+ "persistent", "udp4://router.example.com"}), CommandDefinition::Error);
+ BOOST_CHECK_THROW(cs.parse(std::vector<std::string>{
+ "persistency", "persistent", "udp4://router.example.com"}), CommandDefinition::Error);
+ BOOST_CHECK_THROW(cs.parse(std::vector<std::string>{
+ "remote", "udp4://router.example.com", "persistent"}), CommandDefinition::Error);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Arguments
+
+BOOST_AUTO_TEST_SUITE(ParseValue)
+
+BOOST_AUTO_TEST_CASE(NoneType)
+{
+ CommandDefinition cs("noun", "verb");
+ cs.addArg("a", ArgValueType::NONE, Required::YES, Positional::NO);
+
+ CommandArguments ca;
+
+ ca = cs.parse(std::vector<std::string>{"a"});
+ BOOST_CHECK_EQUAL(ca.size(), 1);
+ BOOST_CHECK(ca.at("a").type() == typeid(bool));
+ BOOST_CHECK_EQUAL(ca.get<bool>("a"), true);
+
+ BOOST_CHECK_THROW(cs.parse(std::vector<std::string>{"a", "value"}), CommandDefinition::Error);
+}
+
+BOOST_AUTO_TEST_CASE(AnyType)
+{
+ CommandDefinition cs("noun", "verb");
+ cs.addArg("a", ArgValueType::ANY, Required::NO, Positional::YES);
+
+ CommandArguments ca;
+
+ ca = cs.parse(std::vector<std::string>{});
+ BOOST_CHECK_EQUAL(ca.size(), 0);
+
+ ca = cs.parse(std::vector<std::string>{"a"});
+ BOOST_CHECK_EQUAL(ca.size(), 1);
+ BOOST_CHECK(ca.at("a").type() == typeid(std::vector<std::string>));
+ std::vector<std::string> values = ca.get<std::vector<std::string>>("a");
+ BOOST_CHECK_EQUAL(values.size(), 1);
+ BOOST_CHECK_EQUAL(values.at(0), "a");
+
+ ca = cs.parse(std::vector<std::string>{"b", "c"});
+ BOOST_CHECK_EQUAL(ca.size(), 1);
+ BOOST_CHECK(ca.at("a").type() == typeid(std::vector<std::string>));
+ values = ca.get<std::vector<std::string>>("a");
+ BOOST_CHECK_EQUAL(values.size(), 2);
+ BOOST_CHECK_EQUAL(values.at(0), "b");
+ BOOST_CHECK_EQUAL(values.at(1), "c");
+}
+
+BOOST_AUTO_TEST_CASE(UnsignedType)
+{
+ CommandDefinition cs("noun", "verb");
+ cs.addArg("a", ArgValueType::UNSIGNED, Required::YES);
+
+ CommandArguments ca;
+
+ ca = cs.parse(std::vector<std::string>{"a", "0"});
+ BOOST_CHECK_EQUAL(ca.size(), 1);
+ BOOST_CHECK(ca.at("a").type() == typeid(uint64_t));
+ BOOST_CHECK_EQUAL(ca.get<uint64_t>("a"), 0);
+
+ ca = cs.parse(std::vector<std::string>{"a", "12923"});
+ BOOST_CHECK_EQUAL(ca.size(), 1);
+ BOOST_CHECK(ca.at("a").type() == typeid(uint64_t));
+ BOOST_CHECK_EQUAL(ca.get<uint64_t>("a"), 12923);
+
+ BOOST_CHECK_THROW(cs.parse(std::vector<std::string>{"a", "-25705"}), CommandDefinition::Error);
+ BOOST_CHECK_THROW(cs.parse(std::vector<std::string>{"a", "not-uint"}), CommandDefinition::Error);
+}
+
+BOOST_AUTO_TEST_CASE(StringType)
+{
+ CommandDefinition cs("noun", "verb");
+ cs.addArg("a", ArgValueType::STRING, Required::YES);
+
+ CommandArguments ca;
+
+ ca = cs.parse(std::vector<std::string>{"a", "hello"});
+ BOOST_CHECK_EQUAL(ca.size(), 1);
+ BOOST_CHECK(ca.at("a").type() == typeid(std::string));
+ BOOST_CHECK_EQUAL(ca.get<std::string>("a"), "hello");
+}
+
+BOOST_AUTO_TEST_CASE(ReportFormatType)
+{
+ CommandDefinition cs("noun", "verb");
+ cs.addArg("a", ArgValueType::REPORT_FORMAT, Required::YES);
+
+ CommandArguments ca;
+
+ ca = cs.parse(std::vector<std::string>{"a", "xml"});
+ BOOST_CHECK_EQUAL(ca.size(), 1);
+ BOOST_CHECK(ca.at("a").type() == typeid(ReportFormat));
+ BOOST_CHECK_EQUAL(ca.get<ReportFormat>("a"), ReportFormat::XML);
+
+ ca = cs.parse(std::vector<std::string>{"a", "text"});
+ BOOST_CHECK_EQUAL(ca.size(), 1);
+ BOOST_CHECK(ca.at("a").type() == typeid(ReportFormat));
+ BOOST_CHECK_EQUAL(ca.get<ReportFormat>("a"), ReportFormat::TEXT);
+
+ BOOST_CHECK_THROW(cs.parse(std::vector<std::string>{"a", "not-fmt"}), CommandDefinition::Error);
+}
+
+BOOST_AUTO_TEST_CASE(NameType)
+{
+ CommandDefinition cs("noun", "verb");
+ cs.addArg("a", ArgValueType::NAME, Required::YES);
+
+ CommandArguments ca;
+
+ ca = cs.parse(std::vector<std::string>{"a", "/n"});
+ BOOST_CHECK_EQUAL(ca.size(), 1);
+ BOOST_CHECK(ca.at("a").type() == typeid(Name));
+ BOOST_CHECK_EQUAL(ca.get<Name>("a"), "/n");
+}
+
+BOOST_AUTO_TEST_CASE(FaceUriType)
+{
+ CommandDefinition cs("noun", "verb");
+ cs.addArg("a", ArgValueType::FACE_URI, Required::YES);
+
+ CommandArguments ca;
+
+ ca = cs.parse(std::vector<std::string>{"a", "udp4://192.0.2.1:6363"});
+ BOOST_CHECK_EQUAL(ca.size(), 1);
+ BOOST_CHECK(ca.at("a").type() == typeid(FaceUri));
+ BOOST_CHECK_EQUAL(ca.get<FaceUri>("a"), FaceUri("udp4://192.0.2.1:6363"));
+
+ BOOST_CHECK_THROW(cs.parse(std::vector<std::string>{"a", "208"}), CommandDefinition::Error);
+ BOOST_CHECK_THROW(cs.parse(std::vector<std::string>{"a", "not-FaceUri"}), CommandDefinition::Error);
+}
+
+BOOST_AUTO_TEST_CASE(FaceIdOrUriType)
+{
+ CommandDefinition cs("noun", "verb");
+ cs.addArg("a", ArgValueType::FACE_ID_OR_URI, Required::YES);
+
+ CommandArguments ca;
+
+ ca = cs.parse(std::vector<std::string>{"a", "208"});
+ BOOST_CHECK_EQUAL(ca.size(), 1);
+ BOOST_CHECK(ca.at("a").type() == typeid(uint64_t));
+ BOOST_CHECK_EQUAL(ca.get<uint64_t>("a"), 208);
+
+ ca = cs.parse(std::vector<std::string>{"a", "udp4://192.0.2.1:6363"});
+ BOOST_CHECK_EQUAL(ca.size(), 1);
+ BOOST_CHECK(ca.at("a").type() == typeid(FaceUri));
+ BOOST_CHECK_EQUAL(ca.get<FaceUri>("a"), FaceUri("udp4://192.0.2.1:6363"));
+
+ BOOST_CHECK_THROW(cs.parse(std::vector<std::string>{"a", "not-FaceUri"}), CommandDefinition::Error);
+}
+
+BOOST_AUTO_TEST_CASE(FacePersistencyType)
+{
+ CommandDefinition cs("noun", "verb");
+ cs.addArg("a", ArgValueType::FACE_PERSISTENCY, Required::YES);
+
+ CommandArguments ca;
+
+ ca = cs.parse(std::vector<std::string>{"a", "persistent"});
+ BOOST_CHECK_EQUAL(ca.size(), 1);
+ BOOST_CHECK(ca.at("a").type() == typeid(FacePersistency));
+ BOOST_CHECK_EQUAL(ca.get<FacePersistency>("a"),
+ FacePersistency::FACE_PERSISTENCY_PERSISTENT);
+
+ ca = cs.parse(std::vector<std::string>{"a", "permanent"});
+ BOOST_CHECK_EQUAL(ca.size(), 1);
+ BOOST_CHECK(ca.at("a").type() == typeid(FacePersistency));
+ BOOST_CHECK_EQUAL(ca.get<FacePersistency>("a"),
+ FacePersistency::FACE_PERSISTENCY_PERMANENT);
+
+ // nfdc does not accept "on-demand"
+ BOOST_CHECK_THROW(cs.parse(std::vector<std::string>{"a", "on-demand"}), CommandDefinition::Error);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // ParseValue
+
+BOOST_AUTO_TEST_SUITE_END() // TestCommandDefinition
+BOOST_AUTO_TEST_SUITE_END() // Nfdc
+
+} // namespace tests
+} // namespace nfdc
+} // namespace tools
+} // namespace nfd
diff --git a/tests/tools/nfdc/command-parser.t.cpp b/tests/tools/nfdc/command-parser.t.cpp
new file mode 100644
index 0000000..435392d
--- /dev/null
+++ b/tests/tools/nfdc/command-parser.t.cpp
@@ -0,0 +1,129 @@
+/* -*- 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/>.
+ */
+
+#include "nfdc/command-parser.hpp"
+
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace tools {
+namespace nfdc {
+namespace tests {
+
+using namespace nfd::tests;
+
+BOOST_AUTO_TEST_SUITE(Nfdc)
+BOOST_FIXTURE_TEST_SUITE(TestCommandParser, BaseFixture)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+ CommandParser parser;
+
+ std::string lastName;
+ auto makeExecute = [&] (const std::string& name) {
+ return [&, name] (const CommandArguments& args) {
+ lastName = name;
+ };
+ };
+
+ CommandDefinition defHelp("help", "");
+ defHelp
+ .addArg("noun", ArgValueType::STRING, Required::NO, Positional::YES)
+ .addArg("verb", ArgValueType::STRING, Required::NO, Positional::YES);
+ parser.addCommand(defHelp, makeExecute("help"), AVAILABLE_IN_ONE_SHOT);
+
+ CommandDefinition defStatusShow("status", "show");
+ parser.addCommand(defStatusShow, makeExecute("status show"));
+ parser.addAlias("status", "show", "list");
+ BOOST_CHECK_THROW(parser.addAlias("status", "show2", "list"), std::out_of_range);
+
+ CommandDefinition defRouteList("route", "list");
+ defRouteList
+ .addArg("nexthop", ArgValueType::FACE_ID_OR_URI, Required::NO, Positional::YES);
+ parser.addCommand(defRouteList, makeExecute("route list"));
+
+ CommandDefinition defRouteAdd("route", "add");
+ defRouteAdd
+ .addArg("prefix", ArgValueType::NAME, Required::YES, Positional::YES)
+ .addArg("nexthop", ArgValueType::FACE_ID_OR_URI, Required::YES, Positional::YES);
+ parser.addCommand(defRouteAdd, makeExecute("route add"));
+ parser.addAlias("route", "add", "add2");
+
+
+ CommandParser::Execute* execute = nullptr;
+ CommandArguments ca;
+
+ std::tie(execute, ca) = parser.parse(std::vector<std::string>{"help"},
+ ParseMode::ONE_SHOT);
+ BOOST_REQUIRE(execute != nullptr);
+ (*execute)(ca);
+ BOOST_CHECK_EQUAL(lastName, "help");
+
+ std::tie(execute, ca) = parser.parse(std::vector<std::string>{"status"},
+ ParseMode::ONE_SHOT);
+ BOOST_REQUIRE(execute != nullptr);
+ (*execute)(ca);
+ BOOST_CHECK_EQUAL(lastName, "status show");
+
+ std::tie(execute, ca) = parser.parse(std::vector<std::string>{"route", "add", "/n", "300"},
+ ParseMode::ONE_SHOT);
+ BOOST_REQUIRE(execute != nullptr);
+ (*execute)(ca);
+ BOOST_CHECK_EQUAL(lastName, "route add");
+ BOOST_CHECK_EQUAL(boost::any_cast<Name>(ca.at("prefix")), "/n");
+ BOOST_CHECK_EQUAL(boost::any_cast<uint64_t>(ca.at("nexthop")), 300);
+
+ std::tie(execute, ca) = parser.parse(std::vector<std::string>{"route", "add2", "/n", "300"},
+ ParseMode::ONE_SHOT);
+ BOOST_REQUIRE(execute != nullptr);
+ (*execute)(ca);
+ BOOST_CHECK_EQUAL(lastName, "route add");
+
+ std::tie(execute, ca) = parser.parse(std::vector<std::string>{"route", "list", "400"},
+ ParseMode::ONE_SHOT);
+ BOOST_REQUIRE(execute != nullptr);
+ (*execute)(ca);
+ BOOST_CHECK_EQUAL(lastName, "route list");
+ BOOST_CHECK_EQUAL(boost::any_cast<uint64_t>(ca.at("nexthop")), 400);
+
+ BOOST_CHECK_THROW(parser.parse(std::vector<std::string>{}, ParseMode::ONE_SHOT),
+ CommandParser::Error);
+ BOOST_CHECK_THROW(parser.parse(std::vector<std::string>{"cant-help"}, ParseMode::ONE_SHOT),
+ CommandParser::Error);
+ BOOST_CHECK_THROW(parser.parse(std::vector<std::string>{"status", "hide"}, ParseMode::ONE_SHOT),
+ CommandParser::Error);
+ BOOST_CHECK_THROW(parser.parse(std::vector<std::string>{"route", "400"}, ParseMode::ONE_SHOT),
+ CommandParser::Error);
+ BOOST_CHECK_THROW(parser.parse(std::vector<std::string>{"route", "add"}, ParseMode::ONE_SHOT),
+ CommandDefinition::Error);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestCommandDefinition
+BOOST_AUTO_TEST_SUITE_END() // Nfdc
+
+} // namespace tests
+} // namespace nfdc
+} // namespace tools
+} // namespace nfd
diff --git a/tools/nfdc/available-commands.cpp b/tools/nfdc/available-commands.cpp
new file mode 100644
index 0000000..3b6a393
--- /dev/null
+++ b/tools/nfdc/available-commands.cpp
@@ -0,0 +1,99 @@
+/* -*- 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/>.
+ */
+
+#include "available-commands.hpp"
+#include "status-report.hpp"
+#include "status-main.hpp"
+#include "legacy-nfdc.hpp"
+
+namespace nfd {
+namespace tools {
+namespace nfdc {
+
+static void
+statusReport(const CommandArguments& ca)
+{
+ int res = 1;
+ ReportFormat fmt = ca.get<ReportFormat>("format", ReportFormat::TEXT);
+ switch (fmt) {
+ case ReportFormat::XML:
+ res = statusMain(std::vector<std::string>{"-x"});
+ break;
+ case ReportFormat::TEXT:
+ res = statusMain(std::vector<std::string>{});
+ break;
+ }
+ exit(res);
+}
+
+static void
+legacyNfdStatus(const CommandArguments& ca)
+{
+ std::vector<std::string> args = ca.get<std::vector<std::string>>("args");
+ int res = statusMain(args);
+ exit(res);
+}
+
+static void
+legacyNfdc(const std::string& subcommand, const CommandArguments& ca)
+{
+ std::vector<std::string> args = ca.get<std::vector<std::string>>("args");
+ int res = legacyNfdcMain(subcommand, args);
+ exit(res);
+}
+
+void
+registerCommands(CommandParser& parser)
+{
+ CommandDefinition defStatusReport("status", "report");
+ defStatusReport
+ .addArg("format", ArgValueType::REPORT_FORMAT, Required::NO, Positional::YES);
+ parser.addCommand(defStatusReport, &statusReport);
+
+ CommandDefinition defLegacyNfdStatus("legacy-nfd-status", "");
+ defLegacyNfdStatus
+ .addArg("args", ArgValueType::ANY, Required::NO, Positional::YES);
+ parser.addCommand(defLegacyNfdStatus, &legacyNfdStatus);
+
+ const std::vector<std::string> legacyNfdcSubcommands{
+ "register",
+ "unregister",
+ "create",
+ "destroy",
+ "set-strategy",
+ "unset-strategy",
+ "add-nexthop",
+ "remove-nexthop"
+ };
+ for (const std::string& subcommand : legacyNfdcSubcommands) {
+ CommandDefinition def(subcommand, "");
+ def.addArg("args", ArgValueType::ANY, Required::NO, Positional::YES);
+ parser.addCommand(def, bind(&legacyNfdc, subcommand, _1));
+ }
+}
+
+} // namespace nfdc
+} // namespace tools
+} // namespace nfd
diff --git a/tools/nfdc/available-commands.hpp b/tools/nfdc/available-commands.hpp
new file mode 100644
index 0000000..a887eec
--- /dev/null
+++ b/tools/nfdc/available-commands.hpp
@@ -0,0 +1,42 @@
+/* -*- 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_AVAILABLE_COMMANDS_HPP
+#define NFD_TOOLS_NFDC_AVAILABLE_COMMANDS_HPP
+
+#include "command-parser.hpp"
+
+namespace nfd {
+namespace tools {
+namespace nfdc {
+
+void
+registerCommands(CommandParser& parser);
+
+} // namespace nfdc
+} // namespace tools
+} // namespace nfd
+
+#endif // NFD_TOOLS_NFDC_AVAILABLE_COMMANDS_HPP
diff --git a/tools/nfdc/command-arguments.hpp b/tools/nfdc/command-arguments.hpp
new file mode 100644
index 0000000..5bf9414
--- /dev/null
+++ b/tools/nfdc/command-arguments.hpp
@@ -0,0 +1,56 @@
+/* -*- 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_ARGUMENTS_HPP
+#define NFD_TOOLS_NFDC_COMMAND_ARGUMENTS_HPP
+
+#include "core/common.hpp"
+#include <boost/any.hpp>
+
+namespace nfd {
+namespace tools {
+namespace nfdc {
+
+/** \brief contains named command arguments
+ */
+class CommandArguments : public std::map<std::string, boost::any>
+{
+public:
+ /** \return the argument value, or a default value if the argument is omitted on command line
+ */
+ template<typename T>
+ T
+ get(const std::string& key, const T& defaultValue = T()) const
+ {
+ auto i = find(key);
+ return i == end() ? defaultValue : boost::any_cast<T>(i->second);
+ }
+};
+
+} // namespace nfdc
+} // namespace tools
+} // namespace nfd
+
+#endif // NFD_TOOLS_NFDC_COMMAND_ARGUMENTS_HPP
diff --git a/tools/nfdc/command-definition.cpp b/tools/nfdc/command-definition.cpp
new file mode 100644
index 0000000..cf11928
--- /dev/null
+++ b/tools/nfdc/command-definition.cpp
@@ -0,0 +1,274 @@
+/* -*- 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/>.
+ */
+
+#include "command-definition.hpp"
+#include "status-report.hpp"
+#include <ndn-cxx/encoding/nfd-constants.hpp>
+#include <ndn-cxx/util/face-uri.hpp>
+#include <ndn-cxx/util/logger.hpp>
+
+namespace nfd {
+namespace tools {
+namespace nfdc {
+
+NDN_LOG_INIT(CommandDefinition);
+
+std::ostream&
+operator<<(std::ostream& os, ArgValueType vt)
+{
+ switch (vt) {
+ case ArgValueType::NONE:
+ return os << "none";
+ case ArgValueType::ANY:
+ return os << "any";
+ case ArgValueType::UNSIGNED:
+ return os << "non-negative integer";
+ case ArgValueType::STRING:
+ return os << "string";
+ case ArgValueType::REPORT_FORMAT:
+ return os << "ReportFormat";
+ case ArgValueType::NAME:
+ return os << "Name";
+ case ArgValueType::FACE_URI:
+ return os << "FaceUri";
+ case ArgValueType::FACE_ID_OR_URI:
+ return os << "FaceId or FaceUri";
+ case ArgValueType::FACE_PERSISTENCY:
+ return os << "FacePersistency";
+ }
+ return os << static_cast<int>(vt);
+}
+
+static std::string
+getMetavarFromType(ArgValueType vt)
+{
+ switch (vt) {
+ case ArgValueType::NONE:
+ return "";
+ case ArgValueType::ANY:
+ return "args";
+ case ArgValueType::UNSIGNED:
+ return "uint";
+ case ArgValueType::STRING:
+ return "str";
+ case ArgValueType::REPORT_FORMAT:
+ return "fmt";
+ case ArgValueType::NAME:
+ return "name";
+ case ArgValueType::FACE_URI:
+ return "uri";
+ case ArgValueType::FACE_ID_OR_URI:
+ return "face";
+ case ArgValueType::FACE_PERSISTENCY:
+ return "persistency";
+ }
+ BOOST_ASSERT(false);
+ return "";
+}
+
+CommandDefinition::CommandDefinition(const std::string& noun, const std::string& verb)
+ : m_noun(noun)
+ , m_verb(verb)
+{
+}
+
+CommandDefinition::~CommandDefinition() = default;
+
+CommandDefinition&
+CommandDefinition::addArg(const std::string& name, ArgValueType valueType,
+ Required isRequired, Positional allowPositional,
+ const std::string& metavar)
+{
+ bool isNew = m_args.emplace(name,
+ Arg{name, valueType, static_cast<bool>(isRequired),
+ metavar.empty() ? getMetavarFromType(valueType) : metavar}).second;
+ BOOST_ASSERT(isNew);
+
+ if (static_cast<bool>(isRequired)) {
+ m_requiredArgs.insert(name);
+ }
+
+ if (static_cast<bool>(allowPositional)) {
+ BOOST_ASSERT(valueType != ArgValueType::NONE);
+ m_positionalArgs.push_back(name);
+ }
+ else {
+ BOOST_ASSERT(valueType != ArgValueType::ANY);
+ }
+
+ return *this;
+}
+
+CommandArguments
+CommandDefinition::parse(const std::vector<std::string>& tokens, size_t start) const
+{
+ CommandArguments ca;
+
+ size_t positionalArgIndex = 0;
+ for (size_t i = start; i < tokens.size(); ++i) {
+ const std::string& token = tokens[i];
+
+ // try to parse as named argument
+ auto namedArg = m_args.find(token);
+ if (namedArg != m_args.end() && namedArg->second.valueType != ArgValueType::ANY) {
+ NDN_LOG_TRACE(token << " is a named argument");
+ const Arg& arg = namedArg->second;
+ if (arg.valueType == ArgValueType::NONE) {
+ ca[arg.name] = true;
+ NDN_LOG_TRACE(token << " is a boolean argument");
+ }
+ else if (i + 1 >= tokens.size()) {
+ BOOST_THROW_EXCEPTION(Error(arg.name + ": " + arg.metavar + " is missing"));
+ }
+ else {
+ const std::string& valueToken = tokens[++i];
+ NDN_LOG_TRACE(arg.name << " has value " << valueToken);
+ try {
+ ca[arg.name] = this->parseValue(arg.valueType, valueToken);
+ }
+ catch (const std::exception& e) {
+ NDN_LOG_TRACE(valueToken << " cannot be parsed as " << arg.valueType);
+ BOOST_THROW_EXCEPTION(Error(arg.name + ": cannot parse '" + valueToken + "' as " +
+ arg.metavar + " (" + e.what() + ")"));
+ }
+ NDN_LOG_TRACE(valueToken << " is parsed as " << arg.valueType);
+ }
+
+ // disallow positional arguments after named argument
+ positionalArgIndex = m_positionalArgs.size();
+ continue;
+ }
+
+ // try to parse as positional argument
+ for (; positionalArgIndex < m_positionalArgs.size(); ++positionalArgIndex) {
+ const Arg& arg = m_args.at(m_positionalArgs[positionalArgIndex]);
+
+ if (arg.valueType == ArgValueType::ANY) {
+ std::vector<std::string> values;
+ std::copy(tokens.begin() + i, tokens.end(), std::back_inserter(values));
+ ca[arg.name] = values;
+ NDN_LOG_TRACE((tokens.size() - i) << " tokens are consumed for " << arg.name);
+ i = tokens.size();
+ break;
+ }
+
+ try {
+ ca[arg.name] = this->parseValue(arg.valueType, token);
+ NDN_LOG_TRACE(token << " is parsed as value for " << arg.name);
+ break;
+ }
+ catch (const std::exception& e) {
+ if (arg.isRequired) { // the current token must be parsed as the value for arg
+ NDN_LOG_TRACE(token << " cannot be parsed as value for " << arg.name);
+ BOOST_THROW_EXCEPTION(Error("cannot parse '" + token + "' as an argument name or as " +
+ arg.metavar + " for " + arg.name + " (" + e.what() + ")"));
+ }
+ else {
+ // the current token may be a value for next positional argument
+ NDN_LOG_TRACE(token << " cannot be parsed as value for " << arg.name);
+ }
+ }
+ }
+
+ if (positionalArgIndex >= m_positionalArgs.size()) {
+ // for loop has reached the end without finding a match,
+ // which means token is not accepted as a value for positional argument
+ BOOST_THROW_EXCEPTION(Error("cannot parse '" + token + "' as an argument name"));
+ }
+
+ // token is accepted; don't parse as the same positional argument again
+ ++positionalArgIndex;
+ }
+
+ for (const std::string& argName : m_requiredArgs) {
+ if (ca.count(argName) == 0) {
+ BOOST_THROW_EXCEPTION(Error(argName + ": required argument is missing"));
+ }
+ }
+
+ return ca;
+}
+
+static ndn::nfd::FacePersistency
+parseFacePersistency(const std::string& s)
+{
+ if (s == "persistent") {
+ return ndn::nfd::FACE_PERSISTENCY_PERSISTENT;
+ }
+ if (s == "permanent") {
+ return ndn::nfd::FACE_PERSISTENCY_PERMANENT;
+ }
+ BOOST_THROW_EXCEPTION(std::invalid_argument("unrecognized FacePersistency"));
+}
+
+boost::any
+CommandDefinition::parseValue(ArgValueType valueType, const std::string& token) const
+{
+ switch (valueType) {
+ case ArgValueType::NONE:
+ case ArgValueType::ANY:
+ BOOST_ASSERT(false);
+ return boost::any();
+
+ case ArgValueType::UNSIGNED: {
+ // boost::lexical_cast<uint64_t> will accept negative number
+ int64_t v = boost::lexical_cast<int64_t>(token);
+ if (v < 0) {
+ BOOST_THROW_EXCEPTION(std::out_of_range("value is negative"));
+ }
+ return static_cast<uint64_t>(v);
+ }
+
+ case ArgValueType::STRING:
+ return token;
+
+ case ArgValueType::REPORT_FORMAT:
+ return parseReportFormat(token);
+
+ case ArgValueType::NAME:
+ return Name(token);
+
+ case ArgValueType::FACE_URI:
+ return ndn::util::FaceUri(token);
+
+ case ArgValueType::FACE_ID_OR_URI:
+ try {
+ return boost::lexical_cast<uint64_t>(token);
+ }
+ catch (const boost::bad_lexical_cast&) {
+ return ndn::util::FaceUri(token);
+ }
+
+ case ArgValueType::FACE_PERSISTENCY:
+ return parseFacePersistency(token);
+ }
+
+ BOOST_ASSERT(false);
+ return boost::any();
+}
+
+} // namespace nfdc
+} // namespace tools
+} // namespace nfd
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
diff --git a/tools/nfdc/command-parser.cpp b/tools/nfdc/command-parser.cpp
new file mode 100644
index 0000000..d91cbc3
--- /dev/null
+++ b/tools/nfdc/command-parser.cpp
@@ -0,0 +1,118 @@
+/* -*- 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/>.
+ */
+
+#include "command-parser.hpp"
+#include <ndn-cxx/util/logger.hpp>
+
+namespace nfd {
+namespace tools {
+namespace nfdc {
+
+NDN_LOG_INIT(CommandParser);
+
+std::ostream&
+operator<<(std::ostream& os, AvailableIn modes)
+{
+ int count = 0;
+
+#define PRINT_BIT(bit, str) \
+ if ((modes & bit) != 0) { \
+ if (++count > 1) { \
+ os << '|'; \
+ } \
+ os << str; \
+ }
+
+ PRINT_BIT(AVAILABLE_IN_ONE_SHOT, "one-shot")
+ PRINT_BIT(AVAILABLE_IN_BATCH, "batch")
+
+#undef PRINT_BIT
+
+ if (count == 0) {
+ os << "none";
+ }
+ return os;
+}
+
+std::ostream&
+operator<<(std::ostream& os, ParseMode mode)
+{
+ switch (mode) {
+ case ParseMode::ONE_SHOT:
+ return os << "one-shot";
+ case ParseMode::BATCH:
+ return os << "batch";
+ }
+ return os << static_cast<int>(mode);
+}
+
+CommandParser&
+CommandParser::addCommand(const CommandDefinition& def, const Execute& execute, AvailableIn modes)
+{
+ BOOST_ASSERT(modes != AVAILABLE_IN_NONE);
+ m_commands[{def.getNoun(), def.getVerb()}].reset(new Command{def, execute, modes});
+ return *this;
+}
+
+CommandParser&
+CommandParser::addAlias(const std::string& noun, const std::string& verb, const std::string& verb2)
+{
+ m_commands[{noun, verb2}] = m_commands.at({noun, verb});
+ return *this;
+}
+
+std::tuple<CommandParser::Execute*, CommandArguments>
+CommandParser::parse(const std::vector<std::string>& tokens, ParseMode mode) const
+{
+ BOOST_ASSERT(mode == ParseMode::ONE_SHOT);
+
+ const std::string& noun = tokens.size() > 0 ? tokens[0] : "";
+ const std::string& verb = tokens.size() > 1 ? tokens[1] : "";
+ size_t nameLen = std::min<size_t>(2, tokens.size());
+
+ auto i = m_commands.find({noun, verb});
+ if (i == m_commands.end()) {
+ if (verb.empty()) {
+ i = m_commands.find({noun, "list"});
+ }
+ else {
+ // help, exit, quit, legacy nfdc commands
+ i = m_commands.find({noun, ""});
+ }
+ nameLen = std::min<size_t>(1, tokens.size());
+ }
+ if (i == m_commands.end() || (i->second->modes & static_cast<AvailableIn>(mode)) == 0) {
+ BOOST_THROW_EXCEPTION(Error("no such command: " + noun + " " + verb));
+ }
+
+ const CommandDefinition& def = i->second->def;
+ NDN_LOG_TRACE("found command " << def.getNoun() << " " << def.getVerb());
+
+ return std::make_tuple(&i->second->execute, def.parse(tokens, nameLen));
+}
+
+} // namespace nfdc
+} // namespace tools
+} // namespace nfd
diff --git a/tools/nfdc/command-parser.hpp b/tools/nfdc/command-parser.hpp
new file mode 100644
index 0000000..7a1dbda
--- /dev/null
+++ b/tools/nfdc/command-parser.hpp
@@ -0,0 +1,114 @@
+/* -*- 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_PARSER_HPP
+#define NFD_TOOLS_NFDC_COMMAND_PARSER_HPP
+
+#include "command-definition.hpp"
+
+namespace nfd {
+namespace tools {
+namespace nfdc {
+
+/** \brief indicates which modes is a command allowed
+ */
+enum AvailableIn : uint8_t {
+ AVAILABLE_IN_NONE = 0,
+ AVAILABLE_IN_ONE_SHOT = 1 << 0,
+ AVAILABLE_IN_BATCH = 1 << 1,
+ AVAILABLE_IN_ALL = 0xff
+};
+
+std::ostream&
+operator<<(std::ostream& os, AvailableIn modes);
+
+/** \brief indicates which mode is the parser operated in
+ */
+enum class ParseMode {
+ ONE_SHOT = AVAILABLE_IN_ONE_SHOT, ///< one-shot mode
+ BATCH = AVAILABLE_IN_BATCH ///< batch mode
+};
+
+std::ostream&
+operator<<(std::ostream& os, ParseMode mode);
+
+/** \brief parses a command
+ */
+class CommandParser : noncopyable
+{
+public:
+ class Error : public std::invalid_argument
+ {
+ public:
+ explicit
+ Error(const std::string& what)
+ : std::invalid_argument(what)
+ {
+ }
+ };
+
+ typedef std::function<void(const CommandArguments&)> Execute;
+
+ /** \brief add an available command
+ * \param def command semantics definition
+ * \param execute a function to execute the command
+ * \param modes parse modes this command should be available in, must not be AVAILABLE_IN_NONE
+ */
+ CommandParser&
+ addCommand(const CommandDefinition& def, const Execute& execute, AvailableIn modes = AVAILABLE_IN_ALL);
+
+ /** \brief add an alias "noun verb2" to existing command "noun verb"
+ * \throw std::out_of_range "noun verb" does not exist
+ */
+ CommandParser&
+ addAlias(const std::string& noun, const std::string& verb, const std::string& verb2);
+
+ /** \brief parse a command line
+ * \param tokens command line
+ * \param mode parser mode, must be ParseMode::ONE_SHOT, other modes are not implemented
+ * \throw Error command is not found
+ * \throw CommandDefinition::Error command arguments are invalid
+ */
+ std::tuple<Execute*, CommandArguments>
+ parse(const std::vector<std::string>& tokens, ParseMode mode) const;
+
+private:
+ typedef std::pair<std::string, std::string> CommandName;
+
+ struct Command
+ {
+ CommandDefinition def;
+ Execute execute;
+ AvailableIn modes;
+ };
+
+ std::map<CommandName, shared_ptr<Command>> m_commands;
+};
+
+} // namespace nfdc
+} // namespace tools
+} // namespace nfd
+
+#endif // NFD_TOOLS_NFDC_COMMAND_PARSER_HPP
diff --git a/tools/nfdc/main.cpp b/tools/nfdc/main.cpp
index 5bc2596..85fe1e9 100644
--- a/tools/nfdc/main.cpp
+++ b/tools/nfdc/main.cpp
@@ -23,8 +23,8 @@
* NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
*/
+#include "available-commands.hpp"
#include "legacy-nfdc.hpp"
-#include "status-main.hpp"
#include "core/version.hpp"
namespace nfd {
@@ -34,29 +34,35 @@
static int
main(int argc, char** argv)
{
- if (argc < 2) {
+ std::vector<std::string> args(argv + 1, argv + argc);
+
+ if (args.empty() || args[0] == "-h") {
legacyNfdcUsage();
return 0;
}
- std::string subcommand(argv[1]);
- std::vector<std::string> args(argv + 2, argv + argc);
-
- if (subcommand == "-h") {
- legacyNfdcUsage();
- return 0;
- }
-
- if (subcommand == "-V") {
+ if (args[0] == "-V") {
std::cout << NFD_VERSION_BUILD_STRING << std::endl;
return 0;
}
- if (subcommand == "legacy-nfd-status") {
- return statusMain(args);
+ CommandParser parser;
+ registerCommands(parser);
+ CommandParser::Execute* execute = nullptr;
+ CommandArguments ca;
+ try {
+ std::tie(execute, ca) = parser.parse(args, ParseMode::ONE_SHOT);
+ }
+ catch (const std::invalid_argument& e) {
+ std::cerr << e.what() << std::endl;
+ return 1;
}
- return legacyNfdcMain(subcommand, args);
+ ///\todo create Face and KeyChain here
+ (*execute)(ca);
+ ///\todo call processEvents here
+ ///\todo return proper exit code here, instead of using exit() in subcommand
+ return 0;
}
} // namespace nfdc