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/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,