peek: code modernization and cleanup

Change-Id: I42d706aa02ab03557d708c729f65716c1c2d3494
diff --git a/tools/peek/ndnpeek/main.cpp b/tools/peek/ndnpeek/main.cpp
index 428634a..1586387 100644
--- a/tools/peek/ndnpeek/main.cpp
+++ b/tools/peek/ndnpeek/main.cpp
@@ -37,42 +37,35 @@
 namespace po = boost::program_options;
 
 static void
-usage(std::ostream& os, const po::options_description& options)
+usage(std::ostream& os, const std::string& program, const po::options_description& options)
 {
-  os << "Usage: ndnpeek [options] ndn:/name\n"
-        "\n"
-        "Fetch one data item matching the name prefix and write it to standard output.\n"
-        "\n"
+  os << "Usage: " << program << " [options] ndn:/name\n"
+     << "\n"
+     << "Fetch one data item matching the specified name and write it to the standard output.\n"
      << options;
 }
 
 static int
 main(int argc, char* argv[])
 {
+  std::string progName(argv[0]);
   PeekOptions options;
 
   po::options_description genericOptDesc("Generic options");
   genericOptDesc.add_options()
-    ("help,h", "print help and exit")
-    ("payload,p", po::bool_switch(&options.wantPayloadOnly),
-        "print payload only, instead of full packet")
-    ("timeout,w", po::value<int>(),
-        "set timeout (in milliseconds)")
-    ("verbose,v", po::bool_switch(&options.isVerbose),
-        "turn on verbose output")
+    ("help,h",    "print help and exit")
+    ("payload,p", po::bool_switch(&options.wantPayloadOnly), "print payload only, instead of full packet")
+    ("timeout,w", po::value<int>(), "set timeout (in milliseconds)")
+    ("verbose,v", po::bool_switch(&options.isVerbose), "turn on verbose output")
     ("version,V", "print version and exit")
   ;
 
   po::options_description interestOptDesc("Interest construction");
   interestOptDesc.add_options()
-    ("prefix,P", po::bool_switch(&options.canBePrefix),
-        "set CanBePrefix")
-    ("fresh,f", po::bool_switch(&options.mustBeFresh),
-        "set MustBeFresh")
-    ("link-file", po::value<std::string>(),
-        "set ForwardingHint from a file")
-    ("lifetime,l", po::value<int>(),
-        "set InterestLifetime (in milliseconds)")
+    ("prefix,P",   po::bool_switch(&options.canBePrefix), "set CanBePrefix")
+    ("fresh,f",    po::bool_switch(&options.mustBeFresh), "set MustBeFresh")
+    ("link-file",  po::value<std::string>(), "set ForwardingHint from a file")
+    ("lifetime,l", po::value<int>(), "set InterestLifetime (in milliseconds)")
   ;
 
   po::options_description visibleOptDesc;
@@ -99,7 +92,7 @@
   }
 
   if (vm.count("help") > 0) {
-    usage(std::cout, visibleOptDesc);
+    usage(std::cout, progName, visibleOptDesc);
     return 0;
   }
 
@@ -108,12 +101,17 @@
     return 0;
   }
 
-  if (vm.count("name") > 0) {
+  if (vm.count("name") == 0) {
+    std::cerr << "ERROR: missing name\n\n";
+    usage(std::cerr, progName, visibleOptDesc);
+    return 2;
+  }
+
+  try {
     options.name = vm["name"].as<std::string>();
   }
-  else {
-    std::cerr << "ERROR: Interest name is missing" << std::endl;
-    usage(std::cerr, visibleOptDesc);
+  catch (const Name::Error& e) {
+    std::cerr << "ERROR: invalid name: " << e.what() << std::endl;
     return 2;
   }
 
@@ -122,19 +120,17 @@
       options.interestLifetime = time::milliseconds(vm["lifetime"].as<int>());
     }
     else {
-      std::cerr << "ERROR: InterestLifetime must be a non-negative integer" << std::endl;
-      usage(std::cerr, visibleOptDesc);
+      std::cerr << "ERROR: lifetime cannot be negative" << std::endl;
       return 2;
     }
   }
 
   if (vm.count("timeout") > 0) {
-    if (vm["timeout"].as<int>() > 0) {
+    if (vm["timeout"].as<int>() >= 0) {
       options.timeout = time::milliseconds(vm["timeout"].as<int>());
     }
     else {
-      std::cerr << "ERROR: Timeout must be a positive integer" << std::endl;
-      usage(std::cerr, visibleOptDesc);
+      std::cerr << "ERROR: timeout cannot be negative" << std::endl;
       return 2;
     }
   }
@@ -142,29 +138,24 @@
   if (vm.count("link-file") > 0) {
     options.link = io::load<Link>(vm["link-file"].as<std::string>());
     if (options.link == nullptr) {
-      std::cerr << "ERROR: Cannot read Link object from the specified file" << std::endl;
-      usage(std::cerr, visibleOptDesc);
+      std::cerr << "ERROR: cannot read Link object from the specified file" << std::endl;
       return 2;
     }
   }
 
-  Face face;
-  NdnPeek program(face, options);
-
   try {
+    Face face;
+    NdnPeek program(face, options);
+
     program.start();
-    face.processEvents(program.getTimeout());
+    face.processEvents();
+
+    return static_cast<int>(program.getResultCode());
   }
   catch (const std::exception& e) {
     std::cerr << "ERROR: " << e.what() << std::endl;
     return 1;
   }
-
-  ResultCode result = program.getResultCode();
-  if (result == ResultCode::TIMEOUT && options.isVerbose) {
-    std::cerr << "TIMEOUT" << std::endl;
-  }
-  return static_cast<int>(result);
 }
 
 } // namespace peek
diff --git a/tools/peek/ndnpeek/ndnpeek.cpp b/tools/peek/ndnpeek/ndnpeek.cpp
index c371882..8ce23f6 100644
--- a/tools/peek/ndnpeek/ndnpeek.cpp
+++ b/tools/peek/ndnpeek/ndnpeek.cpp
@@ -32,37 +32,28 @@
 namespace peek {
 
 NdnPeek::NdnPeek(Face& face, const PeekOptions& options)
-  : m_face(face)
-  , m_options(options)
-  , m_timeout(options.timeout)
-  , m_resultCode(ResultCode::TIMEOUT)
+  : m_options(options)
+  , m_face(face)
+  , m_scheduler(m_face.getIoService())
 {
-  if (m_timeout < 0_ms) {
-    m_timeout = m_options.interestLifetime < 0_ms ?
-                DEFAULT_INTEREST_LIFETIME : m_options.interestLifetime;
-  }
-}
-
-time::milliseconds
-NdnPeek::getTimeout() const
-{
-  return m_timeout;
-}
-
-ResultCode
-NdnPeek::getResultCode() const
-{
-  return m_resultCode;
 }
 
 void
 NdnPeek::start()
 {
-  m_face.expressInterest(createInterest(),
-                         bind(&NdnPeek::onData, this, _2),
-                         bind(&NdnPeek::onNack, this, _2),
-                         nullptr);
-  m_expressInterestTime = time::steady_clock::now();
+  m_pendingInterest = m_face.expressInterest(createInterest(),
+                                             [this] (auto&&, const auto& data) { this->onData(data); },
+                                             [this] (auto&&, const auto& nack) { this->onNack(nack); },
+                                             [this] (auto&&) { this->onTimeout(); });
+
+  if (m_options.timeout) {
+    m_timeoutEvent = m_scheduler.schedule(*m_options.timeout, [this] {
+      m_pendingInterest.cancel();
+      onTimeout();
+    });
+  }
+
+  m_sendTime = time::steady_clock::now();
 }
 
 Interest
@@ -71,11 +62,11 @@
   Interest interest(m_options.name);
   interest.setCanBePrefix(m_options.canBePrefix);
   interest.setMustBeFresh(m_options.mustBeFresh);
-  if (m_options.link != nullptr) {
+  if (m_options.link) {
     interest.setForwardingHint(m_options.link->getDelegationList());
   }
-  if (m_options.interestLifetime >= 0_ms) {
-    interest.setInterestLifetime(m_options.interestLifetime);
+  if (m_options.interestLifetime) {
+    interest.setInterestLifetime(*m_options.interestLifetime);
   }
 
   if (m_options.isVerbose) {
@@ -89,10 +80,11 @@
 NdnPeek::onData(const Data& data)
 {
   m_resultCode = ResultCode::DATA;
+  m_timeoutEvent.cancel();
 
   if (m_options.isVerbose) {
     std::cerr << "DATA: " << data.getName() << "\nRTT: "
-              << time::duration_cast<time::milliseconds>(time::steady_clock::now() - m_expressInterestTime).count()
+              << time::duration_cast<time::milliseconds>(time::steady_clock::now() - m_sendTime).count()
               << " ms" << std::endl;
   }
 
@@ -110,11 +102,12 @@
 NdnPeek::onNack(const lp::Nack& nack)
 {
   m_resultCode = ResultCode::NACK;
-  lp::NackHeader header = nack.getHeader();
+  m_timeoutEvent.cancel();
 
+  lp::NackHeader header = nack.getHeader();
   if (m_options.isVerbose) {
     std::cerr << "NACK: " << header.getReason() << "\nRTT: "
-              << time::duration_cast<time::milliseconds>(time::steady_clock::now() - m_expressInterestTime).count()
+              << time::duration_cast<time::milliseconds>(time::steady_clock::now() - m_sendTime).count()
               << " ms" << std::endl;
   }
 
@@ -127,5 +120,16 @@
   }
 }
 
+void
+NdnPeek::onTimeout()
+{
+  m_resultCode = ResultCode::TIMEOUT;
+  m_timeoutEvent.cancel();
+
+  if (m_options.isVerbose) {
+    std::cerr << "TIMEOUT" << std::endl;
+  }
+}
+
 } // namespace peek
 } // namespace ndn
diff --git a/tools/peek/ndnpeek/ndnpeek.hpp b/tools/peek/ndnpeek/ndnpeek.hpp
index 4eac8d3..a544b37 100644
--- a/tools/peek/ndnpeek/ndnpeek.hpp
+++ b/tools/peek/ndnpeek/ndnpeek.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2018,  Regents of the University of California,
+ * Copyright (c) 2014-2019,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -30,7 +30,9 @@
 #define NDN_TOOLS_NDNPEEK_NDNPEEK_HPP
 
 #include "core/common.hpp"
+
 #include <ndn-cxx/link.hpp>
+#include <ndn-cxx/util/scheduler.hpp>
 
 namespace ndn {
 namespace peek {
@@ -41,23 +43,23 @@
 struct PeekOptions
 {
   // Interest construction options
-  std::string name;
+  Name name;
   bool canBePrefix = false;
   bool mustBeFresh = false;
   shared_ptr<Link> link;
-  time::milliseconds interestLifetime = -1_ms;
+  optional<time::milliseconds> interestLifetime;
 
   // output behavior options
   bool isVerbose = false;
-  time::milliseconds timeout = -1_ms;
   bool wantPayloadOnly = false;
+  optional<time::milliseconds> timeout;
 };
 
 enum class ResultCode {
-  NONE = -1,
+  UNKNOWN = 1,
   DATA = 0,
   NACK = 4,
-  TIMEOUT = 3
+  TIMEOUT = 3,
 };
 
 class NdnPeek : boost::noncopyable
@@ -66,16 +68,13 @@
   NdnPeek(Face& face, const PeekOptions& options);
 
   /**
-   * @return the timeout
-   */
-  time::milliseconds
-  getTimeout() const;
-
-  /**
-   * @return the result of Peek execution
+   * @return the result of NdnPeek execution
    */
   ResultCode
-  getResultCode() const;
+  getResultCode() const
+  {
+    return m_resultCode;
+  }
 
   /**
    * @brief express the Interest
@@ -100,12 +99,20 @@
   void
   onNack(const lp::Nack& nack);
 
+  /**
+   * @brief called when the Interest times out
+   */
+  void
+  onTimeout();
+
 private:
-  Face& m_face;
   const PeekOptions& m_options;
-  time::steady_clock::TimePoint m_expressInterestTime;
-  time::milliseconds m_timeout;
-  ResultCode m_resultCode;
+  Face& m_face;
+  Scheduler m_scheduler;
+  time::steady_clock::TimePoint m_sendTime;
+  ScopedPendingInterestHandle m_pendingInterest;
+  scheduler::ScopedEventId m_timeoutEvent;
+  ResultCode m_resultCode = ResultCode::UNKNOWN;
 };
 
 } // namespace peek