command-definition.cpp
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2014-2017, Regents of the University of California,
4  * Arizona Board of Regents,
5  * Colorado State University,
6  * University Pierre & Marie Curie, Sorbonne University,
7  * Washington University in St. Louis,
8  * Beijing Institute of Technology,
9  * The University of Memphis.
10  *
11  * This file is part of NFD (Named Data Networking Forwarding Daemon).
12  * See AUTHORS.md for complete list of NFD authors and contributors.
13  *
14  * NFD is free software: you can redistribute it and/or modify it under the terms
15  * of the GNU General Public License as published by the Free Software Foundation,
16  * either version 3 of the License, or (at your option) any later version.
17  *
18  * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
19  * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
20  * PURPOSE. See the GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License along with
23  * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
24  */
25 
26 #include "command-definition.hpp"
27 #include <ndn-cxx/util/logger.hpp>
28 
29 namespace nfd {
30 namespace tools {
31 namespace nfdc {
32 
33 NDN_LOG_INIT(nfdc.CommandDefinition);
34 
35 std::ostream&
36 operator<<(std::ostream& os, ArgValueType vt)
37 {
38  switch (vt) {
39  case ArgValueType::NONE:
40  return os << "none";
41  case ArgValueType::ANY:
42  return os << "any";
44  return os << "boolean";
46  return os << "non-negative integer";
48  return os << "string";
50  return os << "ReportFormat";
51  case ArgValueType::NAME:
52  return os << "Name";
54  return os << "FaceUri";
56  return os << "FaceId or FaceUri";
58  return os << "FacePersistency";
60  return os << "RouteOrigin";
61  }
62  return os << static_cast<int>(vt);
63 }
64 
65 static std::string
67 {
68  switch (vt) {
69  case ArgValueType::NONE:
70  return "";
71  case ArgValueType::ANY:
72  return "args";
74  return "bool";
76  return "uint";
78  return "str";
80  return "fmt";
81  case ArgValueType::NAME:
82  return "name";
84  return "uri";
86  return "face";
88  return "persistency";
90  return "origin";
91  }
92  BOOST_ASSERT(false);
93  return "";
94 }
95 
96 CommandDefinition::CommandDefinition(const std::string& noun, const std::string& verb)
97  : m_noun(noun)
98  , m_verb(verb)
99 {
100 }
101 
103 
105 CommandDefinition::addArg(const std::string& name, ArgValueType valueType,
106  Required isRequired, Positional allowPositional,
107  const std::string& metavar)
108 {
109  bool isNew = m_args.emplace(name,
110  Arg{name, valueType, static_cast<bool>(isRequired),
111  metavar.empty() ? getMetavarFromType(valueType) : metavar}).second;
112  BOOST_VERIFY(isNew);
113 
114  if (static_cast<bool>(isRequired)) {
115  m_requiredArgs.insert(name);
116  }
117 
118  if (static_cast<bool>(allowPositional)) {
119  BOOST_ASSERT(valueType != ArgValueType::NONE);
120  m_positionalArgs.push_back(name);
121  }
122  else {
123  BOOST_ASSERT(valueType != ArgValueType::ANY);
124  }
125 
126  return *this;
127 }
128 
130 CommandDefinition::parse(const std::vector<std::string>& tokens, size_t start) const
131 {
132  CommandArguments ca;
133 
134  size_t positionalArgIndex = 0;
135  for (size_t i = start; i < tokens.size(); ++i) {
136  const std::string& token = tokens[i];
137 
138  // try to parse as named argument
139  auto namedArg = m_args.find(token);
140  if (namedArg != m_args.end() && namedArg->second.valueType != ArgValueType::ANY) {
141  NDN_LOG_TRACE(token << " is a named argument");
142  const Arg& arg = namedArg->second;
143  if (arg.valueType == ArgValueType::NONE) {
144  ca[arg.name] = true;
145  NDN_LOG_TRACE(token << " is a no-param argument");
146  }
147  else if (i + 1 >= tokens.size()) {
148  BOOST_THROW_EXCEPTION(Error(arg.name + ": " + arg.metavar + " is missing"));
149  }
150  else {
151  const std::string& valueToken = tokens[++i];
152  NDN_LOG_TRACE(arg.name << " has value " << valueToken);
153  try {
154  ca[arg.name] = this->parseValue(arg.valueType, valueToken);
155  }
156  catch (const std::exception& e) {
157  NDN_LOG_TRACE(valueToken << " cannot be parsed as " << arg.valueType);
158  BOOST_THROW_EXCEPTION(Error(arg.name + ": cannot parse '" + valueToken + "' as " +
159  arg.metavar + " (" + e.what() + ")"));
160  }
161  NDN_LOG_TRACE(valueToken << " is parsed as " << arg.valueType);
162  }
163 
164  // disallow positional arguments after named argument
165  positionalArgIndex = m_positionalArgs.size();
166  continue;
167  }
168 
169  // try to parse as positional argument
170  for (; positionalArgIndex < m_positionalArgs.size(); ++positionalArgIndex) {
171  const Arg& arg = m_args.at(m_positionalArgs[positionalArgIndex]);
172 
173  if (arg.valueType == ArgValueType::ANY) {
174  std::vector<std::string> values;
175  std::copy(tokens.begin() + i, tokens.end(), std::back_inserter(values));
176  ca[arg.name] = values;
177  NDN_LOG_TRACE((tokens.size() - i) << " tokens are consumed for " << arg.name);
178  i = tokens.size();
179  break;
180  }
181 
182  try {
183  ca[arg.name] = this->parseValue(arg.valueType, token);
184  NDN_LOG_TRACE(token << " is parsed as value for " << arg.name);
185  break;
186  }
187  catch (const std::exception& e) {
188  if (arg.isRequired) { // the current token must be parsed as the value for arg
189  NDN_LOG_TRACE(token << " cannot be parsed as value for " << arg.name);
190  BOOST_THROW_EXCEPTION(Error("cannot parse '" + token + "' as an argument name or as " +
191  arg.metavar + " for " + arg.name + " (" + e.what() + ")"));
192  }
193  else {
194  // the current token may be a value for next positional argument
195  NDN_LOG_TRACE(token << " cannot be parsed as value for " << arg.name);
196  }
197  }
198  }
199 
200  if (positionalArgIndex >= m_positionalArgs.size()) {
201  // for loop has reached the end without finding a match,
202  // which means token is not accepted as a value for positional argument
203  BOOST_THROW_EXCEPTION(Error("cannot parse '" + token + "' as an argument name"));
204  }
205 
206  // token is accepted; don't parse as the same positional argument again
207  ++positionalArgIndex;
208  }
209 
210  for (const std::string& argName : m_requiredArgs) {
211  if (ca.count(argName) == 0) {
212  BOOST_THROW_EXCEPTION(Error(argName + ": required argument is missing"));
213  }
214  }
215 
216  return ca;
217 }
218 
219 static bool
220 parseBoolean(const std::string& s)
221 {
222  if (s == "on" || s == "true" || s == "enabled" || s == "yes" || s == "1") {
223  return true;
224  }
225  if (s == "off" || s == "false" || s == "disabled" || s == "no" || s == "0") {
226  return false;
227  }
228  BOOST_THROW_EXCEPTION(std::invalid_argument("unrecognized boolean value '" + s + "'"));
229 }
230 
231 static FacePersistency
232 parseFacePersistency(const std::string& s)
233 {
234  if (s == "persistent") {
235  return FacePersistency::FACE_PERSISTENCY_PERSISTENT;
236  }
237  if (s == "permanent") {
238  return FacePersistency::FACE_PERSISTENCY_PERMANENT;
239  }
240  BOOST_THROW_EXCEPTION(std::invalid_argument("unrecognized FacePersistency '" + s + "'"));
241 }
242 
243 boost::any
244 CommandDefinition::parseValue(ArgValueType valueType, const std::string& token) const
245 {
246  switch (valueType) {
247  case ArgValueType::NONE:
248  case ArgValueType::ANY:
249  BOOST_ASSERT(false);
250  return boost::any();
251 
252  case ArgValueType::BOOLEAN: {
253  return parseBoolean(token);
254  }
255 
256  case ArgValueType::UNSIGNED: {
257  // boost::lexical_cast<uint64_t> will accept negative number
258  int64_t v = boost::lexical_cast<int64_t>(token);
259  if (v < 0) {
260  BOOST_THROW_EXCEPTION(std::out_of_range("value '" + token + "' is negative"));
261  }
262  return static_cast<uint64_t>(v);
263  }
264 
266  return token;
267 
269  return parseReportFormat(token);
270 
271  case ArgValueType::NAME:
272  return Name(token);
273 
275  return FaceUri(token);
276 
278  try {
279  return boost::lexical_cast<uint64_t>(token);
280  }
281  catch (const boost::bad_lexical_cast&) {
282  return FaceUri(token);
283  }
284 
286  return parseFacePersistency(token);
287 
289  return boost::lexical_cast<RouteOrigin>(token);
290  }
291 
292  BOOST_ASSERT(false);
293  return boost::any();
294 }
295 
296 } // namespace nfdc
297 } // namespace tools
298 } // namespace nfd
ReportFormat parseReportFormat(const std::string &s)
boolean argument without value
Required
indicates whether an argument is required
declares semantics of a command
NDN_LOG_INIT(nfdc.CommandDefinition)
ArgValueType
indicates argument value type
static std::string getMetavarFromType(ArgValueType vt)
CommandDefinition(const std::string &noun, const std::string &verb)
std::ostream & operator<<(std::ostream &os, ArgValueType vt)
Copyright (c) 2014-2015, Regents of the University of California, Arizona Board of Regents...
Definition: algorithm.hpp:32
contains named command arguments
face persistency &#39;persistent&#39; or &#39;permanent&#39;
static FacePersistency parseFacePersistency(const std::string &s)
CommandDefinition & addArg(const std::string &name, ArgValueType valueType, Required isRequired=Required::NO, Positional allowPositional=Positional::NO, const std::string &metavar="")
declare an argument
static bool parseBoolean(const std::string &s)
CommandArguments parse(const std::vector< std::string > &tokens, size_t start=0) const
parse a command line
report format &#39;xml&#39; or &#39;text&#39;
Positional
indicates whether an argument can be specified as positional