command-definition.cpp
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
26 #include "command-definition.hpp"
27 #include "status-report.hpp"
28 #include <ndn-cxx/encoding/nfd-constants.hpp>
29 #include <ndn-cxx/util/face-uri.hpp>
30 #include <ndn-cxx/util/logger.hpp>
31 
32 namespace nfd {
33 namespace tools {
34 namespace nfdc {
35 
36 NDN_LOG_INIT(nfdc.CommandDefinition);
37 
38 std::ostream&
39 operator<<(std::ostream& os, ArgValueType vt)
40 {
41  switch (vt) {
42  case ArgValueType::NONE:
43  return os << "none";
44  case ArgValueType::ANY:
45  return os << "any";
47  return os << "non-negative integer";
49  return os << "string";
51  return os << "ReportFormat";
52  case ArgValueType::NAME:
53  return os << "Name";
55  return os << "FaceUri";
57  return os << "FaceId or FaceUri";
59  return os << "FacePersistency";
60  }
61  return os << static_cast<int>(vt);
62 }
63 
64 static std::string
66 {
67  switch (vt) {
68  case ArgValueType::NONE:
69  return "";
70  case ArgValueType::ANY:
71  return "args";
73  return "uint";
75  return "str";
77  return "fmt";
78  case ArgValueType::NAME:
79  return "name";
81  return "uri";
83  return "face";
85  return "persistency";
86  }
87  BOOST_ASSERT(false);
88  return "";
89 }
90 
91 CommandDefinition::CommandDefinition(const std::string& noun, const std::string& verb)
92  : m_noun(noun)
93  , m_verb(verb)
94 {
95 }
96 
98 
100 CommandDefinition::addArg(const std::string& name, ArgValueType valueType,
101  Required isRequired, Positional allowPositional,
102  const std::string& metavar)
103 {
104  bool isNew = m_args.emplace(name,
105  Arg{name, valueType, static_cast<bool>(isRequired),
106  metavar.empty() ? getMetavarFromType(valueType) : metavar}).second;
107  BOOST_VERIFY(isNew);
108 
109  if (static_cast<bool>(isRequired)) {
110  m_requiredArgs.insert(name);
111  }
112 
113  if (static_cast<bool>(allowPositional)) {
114  BOOST_ASSERT(valueType != ArgValueType::NONE);
115  m_positionalArgs.push_back(name);
116  }
117  else {
118  BOOST_ASSERT(valueType != ArgValueType::ANY);
119  }
120 
121  return *this;
122 }
123 
125 CommandDefinition::parse(const std::vector<std::string>& tokens, size_t start) const
126 {
127  CommandArguments ca;
128 
129  size_t positionalArgIndex = 0;
130  for (size_t i = start; i < tokens.size(); ++i) {
131  const std::string& token = tokens[i];
132 
133  // try to parse as named argument
134  auto namedArg = m_args.find(token);
135  if (namedArg != m_args.end() && namedArg->second.valueType != ArgValueType::ANY) {
136  NDN_LOG_TRACE(token << " is a named argument");
137  const Arg& arg = namedArg->second;
138  if (arg.valueType == ArgValueType::NONE) {
139  ca[arg.name] = true;
140  NDN_LOG_TRACE(token << " is a boolean argument");
141  }
142  else if (i + 1 >= tokens.size()) {
143  BOOST_THROW_EXCEPTION(Error(arg.name + ": " + arg.metavar + " is missing"));
144  }
145  else {
146  const std::string& valueToken = tokens[++i];
147  NDN_LOG_TRACE(arg.name << " has value " << valueToken);
148  try {
149  ca[arg.name] = this->parseValue(arg.valueType, valueToken);
150  }
151  catch (const std::exception& e) {
152  NDN_LOG_TRACE(valueToken << " cannot be parsed as " << arg.valueType);
153  BOOST_THROW_EXCEPTION(Error(arg.name + ": cannot parse '" + valueToken + "' as " +
154  arg.metavar + " (" + e.what() + ")"));
155  }
156  NDN_LOG_TRACE(valueToken << " is parsed as " << arg.valueType);
157  }
158 
159  // disallow positional arguments after named argument
160  positionalArgIndex = m_positionalArgs.size();
161  continue;
162  }
163 
164  // try to parse as positional argument
165  for (; positionalArgIndex < m_positionalArgs.size(); ++positionalArgIndex) {
166  const Arg& arg = m_args.at(m_positionalArgs[positionalArgIndex]);
167 
168  if (arg.valueType == ArgValueType::ANY) {
169  std::vector<std::string> values;
170  std::copy(tokens.begin() + i, tokens.end(), std::back_inserter(values));
171  ca[arg.name] = values;
172  NDN_LOG_TRACE((tokens.size() - i) << " tokens are consumed for " << arg.name);
173  i = tokens.size();
174  break;
175  }
176 
177  try {
178  ca[arg.name] = this->parseValue(arg.valueType, token);
179  NDN_LOG_TRACE(token << " is parsed as value for " << arg.name);
180  break;
181  }
182  catch (const std::exception& e) {
183  if (arg.isRequired) { // the current token must be parsed as the value for arg
184  NDN_LOG_TRACE(token << " cannot be parsed as value for " << arg.name);
185  BOOST_THROW_EXCEPTION(Error("cannot parse '" + token + "' as an argument name or as " +
186  arg.metavar + " for " + arg.name + " (" + e.what() + ")"));
187  }
188  else {
189  // the current token may be a value for next positional argument
190  NDN_LOG_TRACE(token << " cannot be parsed as value for " << arg.name);
191  }
192  }
193  }
194 
195  if (positionalArgIndex >= m_positionalArgs.size()) {
196  // for loop has reached the end without finding a match,
197  // which means token is not accepted as a value for positional argument
198  BOOST_THROW_EXCEPTION(Error("cannot parse '" + token + "' as an argument name"));
199  }
200 
201  // token is accepted; don't parse as the same positional argument again
202  ++positionalArgIndex;
203  }
204 
205  for (const std::string& argName : m_requiredArgs) {
206  if (ca.count(argName) == 0) {
207  BOOST_THROW_EXCEPTION(Error(argName + ": required argument is missing"));
208  }
209  }
210 
211  return ca;
212 }
213 
214 static ndn::nfd::FacePersistency
215 parseFacePersistency(const std::string& s)
216 {
217  if (s == "persistent") {
218  return ndn::nfd::FACE_PERSISTENCY_PERSISTENT;
219  }
220  if (s == "permanent") {
221  return ndn::nfd::FACE_PERSISTENCY_PERMANENT;
222  }
223  BOOST_THROW_EXCEPTION(std::invalid_argument("unrecognized FacePersistency"));
224 }
225 
226 boost::any
227 CommandDefinition::parseValue(ArgValueType valueType, const std::string& token) const
228 {
229  switch (valueType) {
230  case ArgValueType::NONE:
231  case ArgValueType::ANY:
232  BOOST_ASSERT(false);
233  return boost::any();
234 
235  case ArgValueType::UNSIGNED: {
236  // boost::lexical_cast<uint64_t> will accept negative number
237  int64_t v = boost::lexical_cast<int64_t>(token);
238  if (v < 0) {
239  BOOST_THROW_EXCEPTION(std::out_of_range("value is negative"));
240  }
241  return static_cast<uint64_t>(v);
242  }
243 
245  return token;
246 
248  return parseReportFormat(token);
249 
250  case ArgValueType::NAME:
251  return Name(token);
252 
254  return ndn::util::FaceUri(token);
255 
257  try {
258  return boost::lexical_cast<uint64_t>(token);
259  }
260  catch (const boost::bad_lexical_cast&) {
261  return ndn::util::FaceUri(token);
262  }
263 
265  return parseFacePersistency(token);
266  }
267 
268  BOOST_ASSERT(false);
269  return boost::any();
270 }
271 
272 } // namespace nfdc
273 } // namespace tools
274 } // 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 'persistent' or 'permanent'
CommandDefinition & addArg(const std::string &name, ArgValueType valueType, Required isRequired=Required::NO, Positional allowPositional=Positional::NO, const std::string &metavar="")
declare an argument
CommandArguments parse(const std::vector< std::string > &tokens, size_t start=0) const
parse a command line
report format 'xml' or 'text'
static ndn::nfd::FacePersistency parseFacePersistency(const std::string &s)
Positional
indicates whether an argument can be specified as positional