mgmt: declare cs/erase command

refs #4318

Change-Id: If34ba8d55a4d46d53f552f4edd748623d4c0e55e
diff --git a/src/mgmt/nfd/control-command.cpp b/src/mgmt/nfd/control-command.cpp
index 9f978f0..556a0ed 100644
--- a/src/mgmt/nfd/control-command.cpp
+++ b/src/mgmt/nfd/control-command.cpp
@@ -270,6 +270,38 @@
     .required(CONTROL_PARAMETER_FLAGS);
 }
 
+CsEraseCommand::CsEraseCommand()
+  : ControlCommand("cs", "erase")
+{
+  m_requestValidator
+    .required(CONTROL_PARAMETER_NAME)
+    .optional(CONTROL_PARAMETER_N_CS_ENTRIES);
+  m_responseValidator
+    .required(CONTROL_PARAMETER_NAME)
+    .optional(CONTROL_PARAMETER_CAPACITY)
+    .required(CONTROL_PARAMETER_N_CS_ENTRIES);
+}
+
+void
+CsEraseCommand::validateRequest(const ControlParameters& parameters) const
+{
+  this->ControlCommand::validateRequest(parameters);
+
+  if (parameters.hasNCsEntries() && parameters.getNCsEntries() == 0) {
+    BOOST_THROW_EXCEPTION(ArgumentError("NCsEntries must be positive"));
+  }
+}
+
+void
+CsEraseCommand::validateResponse(const ControlParameters& parameters) const
+{
+  this->ControlCommand::validateResponse(parameters);
+
+  if (parameters.hasCapacity() && parameters.getCapacity() == 0) {
+    BOOST_THROW_EXCEPTION(ArgumentError("Capacity must be positive"));
+  }
+}
+
 StrategyChoiceSetCommand::StrategyChoiceSetCommand()
   : ControlCommand("strategy-choice", "set")
 {
diff --git a/src/mgmt/nfd/control-command.hpp b/src/mgmt/nfd/control-command.hpp
index 74f0bb1..90d7d38 100644
--- a/src/mgmt/nfd/control-command.hpp
+++ b/src/mgmt/nfd/control-command.hpp
@@ -155,7 +155,7 @@
 /**
  * \ingroup management
  * \brief represents a faces/update command
- * \sa https://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Update-a-face
+ * \sa https://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Update-the-static-properties-of-a-face
  */
 class FaceUpdateCommand : public ControlCommand
 {
@@ -231,7 +231,7 @@
 /**
  * \ingroup management
  * \brief represents a cs/config command
- * \sa https://redmine.named-data.net/projects/nfd/wiki/CsMgmt#Update-config
+ * \sa https://redmine.named-data.net/projects/nfd/wiki/CsMgmt#Update-configuration
  */
 class CsConfigCommand : public ControlCommand
 {
@@ -242,6 +242,24 @@
 
 /**
  * \ingroup management
+ * \brief represents a cs/erase command
+ * \sa https://redmine.named-data.net/projects/nfd/wiki/CsMgmt#Erase-entries
+ */
+class CsEraseCommand : public ControlCommand
+{
+public:
+  CsEraseCommand();
+
+  void
+  validateRequest(const ControlParameters& parameters) const override;
+
+  void
+  validateResponse(const ControlParameters& parameters) const override;
+};
+
+
+/**
+ * \ingroup management
  * \brief represents a strategy-choice/set command
  * \sa https://redmine.named-data.net/projects/nfd/wiki/StrategyChoice#Set-the-strategy-for-a-namespace
  */
diff --git a/src/mgmt/nfd/control-parameters.cpp b/src/mgmt/nfd/control-parameters.cpp
index 1e4d9fe..c0377c5 100644
--- a/src/mgmt/nfd/control-parameters.cpp
+++ b/src/mgmt/nfd/control-parameters.cpp
@@ -76,6 +76,9 @@
   if (this->hasFlags()) {
     totalLength += prependNonNegativeIntegerBlock(encoder, tlv::nfd::Flags, m_flags);
   }
+  if (this->hasNCsEntries()) {
+    totalLength += prependNonNegativeIntegerBlock(encoder, tlv::nfd::NCsEntries, m_nCsEntries);
+  }
   if (this->hasCapacity()) {
     totalLength += prependNonNegativeIntegerBlock(encoder, tlv::nfd::Capacity, m_capacity);
   }
@@ -173,6 +176,12 @@
     m_capacity = readNonNegativeInteger(*val);
   }
 
+  val = m_wire.find(tlv::nfd::NCsEntries);
+  m_hasFields[CONTROL_PARAMETER_N_CS_ENTRIES] = val != m_wire.elements_end();
+  if (this->hasNCsEntries()) {
+    m_nCsEntries = readNonNegativeInteger(*val);
+  }
+
   val = m_wire.find(tlv::nfd::Flags);
   m_hasFields[CONTROL_PARAMETER_FLAGS] = val != m_wire.elements_end();
   if (this->hasFlags()) {
@@ -328,6 +337,10 @@
     os << "Capacity: " << parameters.getCapacity() << ", ";
   }
 
+  if (parameters.hasNCsEntries()) {
+    os << "NCsEntries: " << parameters.getNCsEntries() << ", ";
+  }
+
   if (parameters.hasFlags()) {
     os << "Flags: " << AsHex{parameters.getFlags()} << ", ";
   }
diff --git a/src/mgmt/nfd/control-parameters.hpp b/src/mgmt/nfd/control-parameters.hpp
index ca1d75d..7005e9d 100644
--- a/src/mgmt/nfd/control-parameters.hpp
+++ b/src/mgmt/nfd/control-parameters.hpp
@@ -41,6 +41,7 @@
   CONTROL_PARAMETER_ORIGIN,
   CONTROL_PARAMETER_COST,
   CONTROL_PARAMETER_CAPACITY,
+  CONTROL_PARAMETER_N_CS_ENTRIES,
   CONTROL_PARAMETER_FLAGS,
   CONTROL_PARAMETER_MASK,
   CONTROL_PARAMETER_STRATEGY,
@@ -59,6 +60,7 @@
   "Origin",
   "Cost",
   "Capacity",
+  "NCsEntries",
   "Flags",
   "Mask",
   "Strategy",
@@ -314,6 +316,36 @@
   }
 
   bool
+  hasNCsEntries() const
+  {
+    return m_hasFields[CONTROL_PARAMETER_N_CS_ENTRIES];
+  }
+
+  uint64_t
+  getNCsEntries() const
+  {
+    BOOST_ASSERT(this->hasNCsEntries());
+    return m_nCsEntries;
+  }
+
+  ControlParameters&
+  setNCsEntries(uint64_t nCsEntries)
+  {
+    m_wire.reset();
+    m_nCsEntries = nCsEntries;
+    m_hasFields[CONTROL_PARAMETER_N_CS_ENTRIES] = true;
+    return *this;
+  }
+
+  ControlParameters&
+  unsetNCsEntries()
+  {
+    m_wire.reset();
+    m_hasFields[CONTROL_PARAMETER_N_CS_ENTRIES] = false;
+    return *this;
+  }
+
+  bool
   hasFlags() const
   {
     return m_hasFields[CONTROL_PARAMETER_FLAGS];
@@ -575,6 +607,7 @@
   RouteOrigin         m_origin;
   uint64_t            m_cost;
   uint64_t            m_capacity;
+  uint64_t            m_nCsEntries;
   uint64_t            m_flags;
   uint64_t            m_mask;
   Name                m_strategy;