chunks: refactor Producer options handling

Change-Id: I10331ad9fa33cce1133bc3270cadf65b3e31c93b
diff --git a/tools/chunks/putchunks/ndnputchunks.cpp b/tools/chunks/putchunks/ndnputchunks.cpp
index 7dbb5a8..ce3b88e 100644
--- a/tools/chunks/putchunks/ndnputchunks.cpp
+++ b/tools/chunks/putchunks/ndnputchunks.cpp
@@ -1,8 +1,8 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2016-2017,  Regents of the University of California,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University.
+ * Copyright (c) 2016-2017, Regents of the University of California,
+ *                          Colorado State University,
+ *                          University Pierre & Marie Curie, Sorbonne University.
  *
  * This file is part of ndn-tools (Named Data Networking Essential Tools).
  * See AUTHORS.md for complete list of ndn-tools authors and contributors.
@@ -23,6 +23,7 @@
  * @author Wentao Shang
  * @author Steve DiBenedetto
  * @author Andrea Tosatto
+ * @author Davide Pesavento
  * @author Klaus Schneider
  */
 
@@ -35,48 +36,47 @@
 namespace chunks {
 
 static void
-usage(std::ostream& os, const std::string& programName, const po::options_description& visibleDesc) {
-  os << "Usage: " << programName << " [options] ndn:/name" << std::endl;
-  os << "\nPublish data under specified prefix. "
-     << "Note: this tool expects data from the standard input.\n" << std::endl;
-  os << visibleDesc;
+usage(std::ostream& os, const std::string& programName, const po::options_description& desc)
+{
+  os << "Usage: " << programName << " [options] ndn:/name\n"
+     << "\n"
+     << "Publish data under the specified prefix.\n"
+     << "Note: this tool expects data from the standard input.\n"
+     << "\n"
+     << desc;
 }
 
 static int
 main(int argc, char** argv)
 {
   std::string programName = argv[0];
-  uint64_t freshnessPeriod = 10000;
-  bool printVersion = false;
-  size_t maxChunkSize = MAX_NDN_PACKET_SIZE >> 1;
-  std::string signingStr;
-  bool isVerbose = false;
-  bool isQuiet = false;
   std::string prefix;
+  std::string signingStr;
+  Producer::Options opts;
 
   po::options_description visibleDesc("Options");
   visibleDesc.add_options()
     ("help,h",          "print this help message and exit")
-    ("freshness,f",     po::value<uint64_t>(&freshnessPeriod)->default_value(freshnessPeriod),
-                        "specify FreshnessPeriod, in milliseconds")
-    ("print-data-version,p",  po::bool_switch(&printVersion), "print Data version to the standard output")
-    ("size,s",          po::value<size_t>(&maxChunkSize)->default_value(maxChunkSize),
+    ("freshness,f",     po::value<time::milliseconds::rep>()->default_value(opts.freshnessPeriod.count()),
+                        "FreshnessPeriod of the published Data packets, in milliseconds")
+    ("print-data-version,p", po::bool_switch(&opts.wantShowVersion),
+                             "print Data version to the standard output")
+    ("size,s",          po::value<size_t>(&opts.maxSegmentSize)->default_value(opts.maxSegmentSize),
                         "maximum chunk size, in bytes")
-    ("signing-info,S",  po::value<std::string>(&signingStr)->default_value(signingStr),
-                        "set signing information")
-    ("quiet,q",         po::bool_switch(&isQuiet), "turn off all non-error output")
-    ("verbose,v",       po::bool_switch(&isVerbose), "turn on verbose output (per Interest information)")
+    ("signing-info,S",  po::value<std::string>(&signingStr), "signing information")
+    ("quiet,q",         po::bool_switch(&opts.isQuiet), "turn off all non-error output")
+    ("verbose,v",       po::bool_switch(&opts.isVerbose), "turn on verbose output (per Interest information)")
     ("version,V",       "print program version and exit")
     ;
 
-  po::options_description hiddenDesc("Hidden options");
+  po::options_description hiddenDesc;
   hiddenDesc.add_options()
     ("ndn-name,n", po::value<std::string>(&prefix), "NDN name for the served content");
 
   po::positional_options_description p;
   p.add("ndn-name", -1);
 
-  po::options_description optDesc("Allowed options");
+  po::options_description optDesc;
   optDesc.add(visibleDesc).add(hiddenDesc);
 
   po::variables_map vm;
@@ -108,30 +108,34 @@
     return 2;
   }
 
-  if (maxChunkSize < 1 || maxChunkSize > MAX_NDN_PACKET_SIZE) {
+  opts.freshnessPeriod = time::milliseconds(vm["freshness"].as<time::milliseconds::rep>());
+  if (opts.freshnessPeriod < time::milliseconds::zero()) {
+    std::cerr << "ERROR: FreshnessPeriod cannot be negative" << std::endl;
+    return 2;
+  }
+
+  if (opts.maxSegmentSize < 1 || opts.maxSegmentSize > MAX_NDN_PACKET_SIZE) {
     std::cerr << "ERROR: Maximum chunk size must be between 1 and " << MAX_NDN_PACKET_SIZE << std::endl;
     return 2;
   }
 
-  if (isQuiet && isVerbose) {
-    std::cerr << "ERROR: Cannot use flags -q and -v at the same time\n";
-    return 2;
-  }
-
-  security::SigningInfo signingInfo;
   try {
-    signingInfo = security::SigningInfo(signingStr);
+    opts.signingInfo = security::SigningInfo(signingStr);
   }
   catch (const std::invalid_argument& e) {
     std::cerr << "ERROR: " << e.what() << std::endl;
     return 2;
   }
 
+  if (opts.isQuiet && opts.isVerbose) {
+    std::cerr << "ERROR: Cannot be quiet and verbose at the same time" << std::endl;
+    return 2;
+  }
+
   try {
     Face face;
     KeyChain keyChain;
-    Producer producer(prefix, face, keyChain, signingInfo, time::milliseconds(freshnessPeriod),
-                      maxChunkSize, isQuiet, isVerbose, printVersion, std::cin);
+    Producer producer(prefix, face, keyChain, std::cin, opts);
     producer.run();
   }
   catch (const std::exception& e) {
diff --git a/tools/chunks/putchunks/producer.cpp b/tools/chunks/putchunks/producer.cpp
index 5e50900..3fb14c7 100644
--- a/tools/chunks/putchunks/producer.cpp
+++ b/tools/chunks/putchunks/producer.cpp
@@ -1,8 +1,8 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2016-2017,  Regents of the University of California,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University.
+ * Copyright (c) 2016-2017, Regents of the University of California,
+ *                          Colorado State University,
+ *                          University Pierre & Marie Curie, Sorbonne University.
  *
  * This file is part of ndn-tools (Named Data Networking Essential Tools).
  * See AUTHORS.md for complete list of ndn-tools authors and contributors.
@@ -23,6 +23,7 @@
  * @author Wentao Shang
  * @author Steve DiBenedetto
  * @author Andrea Tosatto
+ * @author Davide Pesavento
  * @author Klaus Schneider
  */
 
@@ -31,23 +32,11 @@
 namespace ndn {
 namespace chunks {
 
-Producer::Producer(const Name& prefix,
-                   Face& face,
-                   KeyChain& keyChain,
-                   const security::SigningInfo& signingInfo,
-                   time::milliseconds freshnessPeriod,
-                   size_t maxSegmentSize,
-                   bool isQuiet,
-                   bool isVerbose,
-                   bool needToPrintVersion,
-                   std::istream& is)
+Producer::Producer(const Name& prefix, Face& face, KeyChain& keyChain, std::istream& is,
+                   const Options& opts)
   : m_face(face)
   , m_keyChain(keyChain)
-  , m_signingInfo(signingInfo)
-  , m_freshnessPeriod(freshnessPeriod)
-  , m_maxSegmentSize(maxSegmentSize)
-  , m_isQuiet(isQuiet)
-  , m_isVerbose(isVerbose)
+  , m_options(opts)
 {
   if (prefix.size() > 0 && prefix[-1].isVersion()) {
     m_prefix = prefix.getPrefix(-1);
@@ -60,7 +49,7 @@
 
   populateStore(is);
 
-  if (needToPrintVersion)
+  if (m_options.wantShowVersion)
     std::cout << m_versionedPrefix[-1] << std::endl;
 
   m_face.setInterestFilter(m_prefix,
@@ -68,7 +57,7 @@
                            RegisterPrefixSuccessCallback(),
                            bind(&Producer::onRegisterFailed, this, _1, _2));
 
-  if (!m_isQuiet)
+  if (!m_options.isQuiet)
     std::cerr << "Data published with name: " << m_versionedPrefix << std::endl;
 }
 
@@ -83,7 +72,7 @@
 {
   BOOST_ASSERT(m_store.size() > 0);
 
-  if (m_isVerbose)
+  if (m_options.isVerbose)
     std::cerr << "Interest: " << interest << std::endl;
 
   const Name& name = interest.getName();
@@ -104,7 +93,7 @@
   }
 
   if (data != nullptr) {
-    if (m_isVerbose)
+    if (m_options.isVerbose)
       std::cerr << "Data: " << *data << std::endl;
 
     m_face.put(*data);
@@ -114,37 +103,37 @@
 void
 Producer::populateStore(std::istream& is)
 {
-  BOOST_ASSERT(m_store.size() == 0);
+  BOOST_ASSERT(m_store.empty());
 
-  if (!m_isQuiet)
+  if (!m_options.isQuiet)
     std::cerr << "Loading input ..." << std::endl;
 
-  std::vector<uint8_t> buffer(m_maxSegmentSize);
+  std::vector<uint8_t> buffer(m_options.maxSegmentSize);
   while (is.good()) {
     is.read(reinterpret_cast<char*>(buffer.data()), buffer.size());
     const auto nCharsRead = is.gcount();
+
     if (nCharsRead > 0) {
       auto data = make_shared<Data>(Name(m_versionedPrefix).appendSegment(m_store.size()));
-      data->setFreshnessPeriod(m_freshnessPeriod);
-      data->setContent(&buffer[0], nCharsRead);
-
+      data->setFreshnessPeriod(m_options.freshnessPeriod);
+      data->setContent(buffer.data(), static_cast<size_t>(nCharsRead));
       m_store.push_back(data);
     }
   }
 
   if (m_store.empty()) {
     auto data = make_shared<Data>(Name(m_versionedPrefix).appendSegment(0));
-    data->setFreshnessPeriod(m_freshnessPeriod);
+    data->setFreshnessPeriod(m_options.freshnessPeriod);
     m_store.push_back(data);
   }
 
   auto finalBlockId = name::Component::fromSegment(m_store.size() - 1);
   for (const auto& data : m_store) {
     data->setFinalBlockId(finalBlockId);
-    m_keyChain.sign(*data, m_signingInfo);
+    m_keyChain.sign(*data, m_options.signingInfo);
   }
 
-  if (!m_isQuiet)
+  if (!m_options.isQuiet)
     std::cerr << "Created " << m_store.size() << " chunks for prefix " << m_prefix << std::endl;
 }
 
diff --git a/tools/chunks/putchunks/producer.hpp b/tools/chunks/putchunks/producer.hpp
index 86dcad7..f498ec7 100644
--- a/tools/chunks/putchunks/producer.hpp
+++ b/tools/chunks/putchunks/producer.hpp
@@ -1,8 +1,8 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2016-2017,  Regents of the University of California,
- *                      Colorado State University,
- *                      University Pierre & Marie Curie, Sorbonne University.
+ * Copyright (c) 2016-2017, Regents of the University of California,
+ *                          Colorado State University,
+ *                          University Pierre & Marie Curie, Sorbonne University.
  *
  * This file is part of ndn-tools (Named Data Networking Essential Tools).
  * See AUTHORS.md for complete list of ndn-tools authors and contributors.
@@ -23,6 +23,7 @@
  * @author Wentao Shang
  * @author Steve DiBenedetto
  * @author Andrea Tosatto
+ * @author Davide Pesavento
  * @author Klaus Schneider
  */
 
@@ -35,7 +36,7 @@
 namespace chunks {
 
 /**
- * @brief Segmented version Producer
+ * @brief Segmented & versioned data publisher
  *
  * Packetizes and publishes data from an input stream under /prefix/<version>/<segment number>.
  * The current time is used as the version number. The store has always at least one element (also
@@ -44,16 +45,25 @@
 class Producer : noncopyable
 {
 public:
+  struct Options
+  {
+    security::SigningInfo signingInfo;
+    time::milliseconds freshnessPeriod{10000};
+    size_t maxSegmentSize = MAX_NDN_PACKET_SIZE >> 1;
+    bool isQuiet = false;
+    bool isVerbose = false;
+    bool wantShowVersion = false;
+  };
+
+public:
   /**
    * @brief Create the Producer
    *
-   * @prefix prefix used to publish data, if the last component of prefix is not a version number
-   *         the current time is used as version number.
+   * @param prefix prefix used to publish data; if the last component is not a valid
+   *               version number, the current system time is used as version number.
    */
-  Producer(const Name& prefix, Face& face, KeyChain& keyChain,
-           const security::SigningInfo& signingInfo, time::milliseconds freshnessPeriod,
-           size_t maxSegmentSize, bool isQuiet = false, bool isVerbose = false,
-           bool needToPrintVersion = false, std::istream& is = std::cin);
+  Producer(const Name& prefix, Face& face, KeyChain& keyChain, std::istream& is,
+           const Options& opts);
 
   /**
    * @brief Run the Producer
@@ -62,9 +72,6 @@
   run();
 
 private:
-  void
-  onInterest(const Interest& interest);
-
   /**
    * @brief Split the input stream in data packets and save them to the store
    *
@@ -79,6 +86,9 @@
   populateStore(std::istream& is);
 
   void
+  onInterest(const Interest& interest);
+
+  void
   onRegisterFailed(const Name& prefix, const std::string& reason);
 
 PUBLIC_WITH_TESTS_ELSE_PRIVATE:
@@ -89,11 +99,7 @@
   Name m_versionedPrefix;
   Face& m_face;
   KeyChain& m_keyChain;
-  security::SigningInfo m_signingInfo;
-  time::milliseconds m_freshnessPeriod;
-  size_t m_maxSegmentSize;
-  bool m_isQuiet;
-  bool m_isVerbose;
+  const Options m_options;
 };
 
 } // namespace chunks