tools: nfdc route add command

This commit also simplifies 'nfdc face destroy' and its test case.

'nfdc register' is deprecated in favor of 'nfdc route add'.

refs #3866

Change-Id: I1de5cc0bc956d57b0793da920c1e87b3580a3297
diff --git a/tools/nfdc/available-commands.cpp b/tools/nfdc/available-commands.cpp
index 586da48..d6782ac 100644
--- a/tools/nfdc/available-commands.cpp
+++ b/tools/nfdc/available-commands.cpp
@@ -24,11 +24,12 @@
  */
 
 #include "available-commands.hpp"
-#include "help.hpp"
-#include "status.hpp"
 #include "face-module.hpp"
-#include "legacy-status.hpp"
+#include "help.hpp"
 #include "legacy-nfdc.hpp"
+#include "legacy-status.hpp"
+#include "rib-module.hpp"
+#include "status.hpp"
 
 namespace nfd {
 namespace tools {
@@ -40,6 +41,7 @@
   registerHelpCommand(parser);
   registerStatusCommands(parser);
   FaceModule::registerCommands(parser);
+  RibModule::registerCommands(parser);
 
   registerLegacyStatusCommand(parser);
 
@@ -50,7 +52,7 @@
     std::string replacementCommand; ///< replacement for deprecated legacy subcommand
   };
   const std::vector<LegacyNfdcCommandDefinition> legacyNfdcSubcommands{
-    {"register", "register a prefix", ""},
+    {"register", "register a prefix", "route add"},
     {"unregister", "unregister a prefix", ""},
     {"create", "create a face", "face create"},
     {"destroy", "destroy a face", "face destroy"},
diff --git a/tools/nfdc/face-module.cpp b/tools/nfdc/face-module.cpp
index 216ded6..f0c9dfc 100644
--- a/tools/nfdc/face-module.cpp
+++ b/tools/nfdc/face-module.cpp
@@ -209,17 +209,10 @@
 void
 FaceModule::destroy(ExecuteContext& ctx)
 {
-  const boost::any faceIdOrUri = ctx.args.at("face");
+  const boost::any& faceIdOrUri = ctx.args.at("face");
 
   FindFace findFace(ctx);
-  FindFace::Code res = FindFace::Code::ERROR;
-  const uint64_t* faceId = boost::any_cast<uint64_t>(&faceIdOrUri);
-  if (faceId != nullptr) {
-    res = findFace.execute(*faceId);
-  }
-  else {
-    res = findFace.execute(boost::any_cast<FaceUri>(faceIdOrUri));
-  }
+  FindFace::Code res = findFace.execute(faceIdOrUri);
 
   ctx.exitCode = static_cast<int>(res);
   switch (res) {
diff --git a/tools/nfdc/find-face.cpp b/tools/nfdc/find-face.cpp
index 520ea92..d877e81 100644
--- a/tools/nfdc/find-face.cpp
+++ b/tools/nfdc/find-face.cpp
@@ -55,6 +55,18 @@
 }
 
 FindFace::Code
+FindFace::execute(const boost::any& faceIdOrUri)
+{
+  const uint64_t* faceId = boost::any_cast<uint64_t>(&faceIdOrUri);
+  if (faceId != nullptr) {
+    return this->execute(*faceId);
+  }
+  else {
+    return this->execute(boost::any_cast<FaceUri>(faceIdOrUri));
+  }
+}
+
+FindFace::Code
 FindFace::execute(const FaceQueryFilter& filter, bool allowMulti)
 {
   BOOST_ASSERT(m_res == Code::NOT_STARTED);
diff --git a/tools/nfdc/find-face.hpp b/tools/nfdc/find-face.hpp
index 57eb0f0..45c3f1e 100644
--- a/tools/nfdc/find-face.hpp
+++ b/tools/nfdc/find-face.hpp
@@ -70,6 +70,14 @@
   Code
   execute(uint64_t faceId);
 
+  /** \brief find face by FaceId or FaceUri
+   *  \param faceIdOrUri a boost::any that contains uint64_t or FaceUri
+   *  \note allowMulti will be set to false
+   *  \throw boost::bad_any_cast faceIdOrUri is neither uint64_t nor FaceUri
+   */
+  Code
+  execute(const boost::any& faceIdOrUri);
+
   /** \brief find face by FaceQueryFilter
    *  \pre execute has not been invoked
    */
diff --git a/tools/nfdc/rib-module.cpp b/tools/nfdc/rib-module.cpp
index 454dc1e..b85410c 100644
--- a/tools/nfdc/rib-module.cpp
+++ b/tools/nfdc/rib-module.cpp
@@ -24,6 +24,7 @@
  */
 
 #include "rib-module.hpp"
+#include "find-face.hpp"
 #include "format-helpers.hpp"
 
 namespace nfd {
@@ -31,6 +32,90 @@
 namespace nfdc {
 
 void
+RibModule::registerCommands(CommandParser& parser)
+{
+  CommandDefinition defRouteAdd("route", "add");
+  defRouteAdd
+    .setTitle("add a route")
+    .addArg("prefix", ArgValueType::NAME, Required::YES, Positional::YES)
+    .addArg("nexthop", ArgValueType::FACE_ID_OR_URI, Required::YES, Positional::YES)
+    .addArg("origin", ArgValueType::UNSIGNED, Required::NO, Positional::NO)
+    .addArg("cost", ArgValueType::UNSIGNED, Required::NO, Positional::NO)
+    .addArg("no-inherit", ArgValueType::NONE, Required::NO, Positional::NO)
+    .addArg("capture", ArgValueType::NONE, Required::NO, Positional::NO)
+    .addArg("expires", ArgValueType::UNSIGNED, Required::NO, Positional::NO);
+  parser.addCommand(defRouteAdd, &RibModule::add);
+}
+
+void
+RibModule::add(ExecuteContext& ctx)
+{
+  auto prefix = ctx.args.get<Name>("prefix");
+  const boost::any& nexthop = ctx.args.at("nexthop");
+  auto origin = ctx.args.get<uint64_t>("origin", ndn::nfd::ROUTE_ORIGIN_STATIC);
+  auto cost = ctx.args.get<uint64_t>("cost", 0);
+  bool wantChildInherit = !ctx.args.get<bool>("no-inherit", false);
+  bool wantCapture = ctx.args.get<bool>("capture", false);
+  auto expiresMillis = ctx.args.getOptional<uint64_t>("expires");
+
+  FindFace findFace(ctx);
+  FindFace::Code res = findFace.execute(nexthop);
+
+  ctx.exitCode = static_cast<int>(res);
+  switch (res) {
+    case FindFace::Code::OK:
+      break;
+    case FindFace::Code::ERROR:
+    case FindFace::Code::CANONIZE_ERROR:
+    case FindFace::Code::NOT_FOUND:
+      ctx.err << findFace.getErrorReason() << '\n';
+      return;
+    case FindFace::Code::AMBIGUOUS:
+      ctx.err << "Multiple faces match specified remote FaceUri. Re-run the command with a FaceId:";
+      findFace.printDisambiguation(ctx.err, FindFace::DisambiguationStyle::LOCAL_URI);
+      ctx.err << '\n';
+      return;
+    default:
+      BOOST_ASSERT_MSG(false, "unexpected FindFace result");
+      return;
+  }
+
+  ControlParameters registerParams;
+  registerParams
+    .setName(prefix)
+    .setFaceId(findFace.getFaceId())
+    .setOrigin(origin)
+    .setCost(cost)
+    .setFlags((wantChildInherit ? ndn::nfd::ROUTE_FLAG_CHILD_INHERIT : 0) |
+              (wantCapture ? ndn::nfd::ROUTE_FLAG_CAPTURE : 0));
+  if (expiresMillis) {
+    registerParams.setExpirationPeriod(time::milliseconds(*expiresMillis));
+  }
+
+  ctx.controller.start<ndn::nfd::RibRegisterCommand>(
+    registerParams,
+    [&] (const ControlParameters& resp) {
+      ctx.out << "route-add-accepted ";
+      text::ItemAttributes ia;
+      ctx.out << ia("prefix") << resp.getName()
+              << ia("nexthop") << resp.getFaceId()
+              << ia("origin") << resp.getOrigin()
+              << ia("cost") << resp.getCost()
+              << ia("flags") << static_cast<ndn::nfd::RouteFlags>(resp.getFlags());
+      if (resp.hasExpirationPeriod()) {
+        ctx.out << ia("expires") << resp.getExpirationPeriod().count() << "ms\n";
+      }
+      else {
+        ctx.out<< ia("expires") << "never\n";
+      }
+    },
+    ctx.makeCommandFailureHandler("adding route"),
+    ctx.makeCommandOptions());
+
+  ctx.face.processEvents();
+}
+
+void
 RibModule::fetchStatus(Controller& controller,
                        const function<void()>& onSuccess,
                        const Controller::DatasetFailCallback& onFailure,
diff --git a/tools/nfdc/rib-module.hpp b/tools/nfdc/rib-module.hpp
index 85230aa..cca1ef4 100644
--- a/tools/nfdc/rib-module.hpp
+++ b/tools/nfdc/rib-module.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2016,  Regents of the University of California,
+ * Copyright (c) 2014-2017,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -27,6 +27,7 @@
 #define NFD_TOOLS_NFDC_RIB_MODULE_HPP
 
 #include "module.hpp"
+#include "command-parser.hpp"
 
 namespace nfd {
 namespace tools {
@@ -41,13 +42,23 @@
 class RibModule : public Module, noncopyable
 {
 public:
-  virtual void
+  /** \brief register 'route list', 'route show', 'route add', 'route remove' commands
+   */
+  static void
+  registerCommands(CommandParser& parser);
+
+  /** \brief the 'route add' command
+   */
+  static void
+  add(ExecuteContext& ctx);
+
+  void
   fetchStatus(Controller& controller,
               const function<void()>& onSuccess,
               const Controller::DatasetFailCallback& onFailure,
               const CommandOptions& options) override;
 
-  virtual void
+  void
   formatStatusXml(std::ostream& os) const override;
 
   /** \brief format a single status item as XML
@@ -57,7 +68,7 @@
   void
   formatItemXml(std::ostream& os, const RibEntry& item) const;
 
-  virtual void
+  void
   formatStatusText(std::ostream& os) const override;
 
   /** \brief format a single status item as text