Loading...
Searching...
No Matches
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-2024, 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
27#include "status-report.hpp"
28
29#include <ndn-cxx/net/face-uri.hpp>
30#include <ndn-cxx/util/backports.hpp>
31#include <ndn-cxx/util/logger.hpp>
32
33#include <boost/lexical_cast.hpp>
34
35namespace nfd::tools::nfdc {
36
37NDN_LOG_INIT(nfdc.CommandDefinition);
38
39std::ostream&
40operator<<(std::ostream& os, ArgValueType vt)
41{
42 switch (vt) {
44 return os << "none";
46 return os << "any";
48 return os << "boolean";
50 return os << "non-negative integer";
52 return os << "string";
54 return os << "ReportFormat";
56 return os << "Name";
58 return os << "FaceUri";
60 return os << "FaceId or FaceUri";
62 return os << "FacePersistency";
64 return os << "RouteOrigin";
65 }
66 return os << static_cast<int>(vt);
67}
68
69static std::string
71{
72 switch (vt) {
74 return "";
76 return "args";
78 return "bool";
80 return "uint";
82 return "str";
84 return "fmt";
86 return "name";
88 return "uri";
90 return "face";
92 return "persistency";
94 return "origin";
95 }
96 NDN_CXX_UNREACHABLE;
97}
98
99CommandDefinition::CommandDefinition(std::string_view noun, std::string_view verb)
100 : m_noun(noun)
101 , m_verb(verb)
102{
103}
104
106
108CommandDefinition::addArg(const std::string& name, ArgValueType valueType,
109 Required isRequired, Positional allowPositional,
110 const std::string& metavar)
111{
112 bool isNew = m_args.emplace(name,
113 Arg{name, valueType, static_cast<bool>(isRequired),
114 metavar.empty() ? getMetavarFromType(valueType) : metavar}).second;
115 BOOST_VERIFY(isNew);
116
117 if (static_cast<bool>(isRequired)) {
118 m_requiredArgs.insert(name);
119 }
120
121 if (static_cast<bool>(allowPositional)) {
122 BOOST_ASSERT(valueType != ArgValueType::NONE);
123 m_positionalArgs.push_back(name);
124 }
125 else {
126 BOOST_ASSERT(valueType != ArgValueType::ANY);
127 }
128
129 return *this;
130}
131
133CommandDefinition::parse(const std::vector<std::string>& tokens, size_t start) const
134{
136
137 size_t positionalArgIndex = 0;
138 for (size_t i = start; i < tokens.size(); ++i) {
139 const std::string& token = tokens[i];
140
141 // try to parse as named argument
142 auto namedArg = m_args.find(token);
143 if (namedArg != m_args.end() && namedArg->second.valueType != ArgValueType::ANY) {
144 NDN_LOG_TRACE(token << " is a named argument");
145 const Arg& arg = namedArg->second;
146 if (arg.valueType == ArgValueType::NONE) {
147 ca[arg.name] = true;
148 NDN_LOG_TRACE(token << " is a no-param argument");
149 }
150 else if (i + 1 >= tokens.size()) {
151 NDN_THROW(Error(arg.name + ": " + arg.metavar + " is missing"));
152 }
153 else {
154 const std::string& valueToken = tokens[++i];
155 NDN_LOG_TRACE(arg.name << " has value " << valueToken);
156 try {
157 ca[arg.name] = parseValue(arg.valueType, valueToken);
158 }
159 catch (const std::exception& e) {
160 NDN_LOG_TRACE(valueToken << " cannot be parsed as " << arg.valueType);
161 NDN_THROW_NESTED(Error(arg.name + ": cannot parse '" + valueToken + "' as " +
162 arg.metavar + " (" + e.what() + ")"));
163 }
164 NDN_LOG_TRACE(valueToken << " is parsed as " << arg.valueType);
165 }
166
167 // disallow positional arguments after named argument
168 positionalArgIndex = m_positionalArgs.size();
169 continue;
170 }
171
172 // try to parse as positional argument
173 for (; positionalArgIndex < m_positionalArgs.size(); ++positionalArgIndex) {
174 const Arg& arg = m_args.at(m_positionalArgs[positionalArgIndex]);
175
176 if (arg.valueType == ArgValueType::ANY) {
177 std::vector<std::string> values;
178 std::copy(tokens.begin() + i, tokens.end(), std::back_inserter(values));
179 ca[arg.name] = values;
180 NDN_LOG_TRACE((tokens.size() - i) << " tokens are consumed for " << arg.name);
181 i = tokens.size();
182 break;
183 }
184
185 try {
186 ca[arg.name] = parseValue(arg.valueType, token);
187 NDN_LOG_TRACE(token << " is parsed as value for " << arg.name);
188 break;
189 }
190 catch (const std::exception& e) {
191 if (arg.isRequired) { // the current token must be parsed as the value for arg
192 NDN_LOG_TRACE(token << " cannot be parsed as value for " << arg.name);
193 NDN_THROW_NESTED(Error("cannot parse '" + token + "' as an argument name or as " +
194 arg.metavar + " for " + arg.name + " (" + e.what() + ")"));
195 }
196 else {
197 // the current token may be a value for next positional argument
198 NDN_LOG_TRACE(token << " cannot be parsed as value for " << arg.name);
199 }
200 }
201 }
202
203 if (positionalArgIndex >= m_positionalArgs.size()) {
204 // for loop has reached the end without finding a match,
205 // which means token is not accepted as a value for positional argument
206 NDN_THROW(Error("cannot parse '" + token + "' as an argument name"));
207 }
208
209 // token is accepted; don't parse as the same positional argument again
210 ++positionalArgIndex;
211 }
212
213 for (const auto& argName : m_requiredArgs) {
214 if (ca.count(argName) == 0) {
215 NDN_THROW(Error(argName + ": required argument is missing"));
216 }
217 }
218
219 return ca;
220}
221
222static bool
223parseBoolean(const std::string& s)
224{
225 if (s == "on" || s == "true" || s == "enabled" || s == "yes" || s == "1") {
226 return true;
227 }
228 if (s == "off" || s == "false" || s == "disabled" || s == "no" || s == "0") {
229 return false;
230 }
231 NDN_THROW(std::invalid_argument("unrecognized boolean value '" + s + "'"));
232}
233
234static ReportFormat
235parseReportFormat(const std::string& s)
236{
237 if (s == "xml") {
238 return ReportFormat::XML;
239 }
240 if (s == "text") {
241 return ReportFormat::TEXT;
242 }
243 NDN_THROW(std::invalid_argument("unrecognized ReportFormat '" + s + "'"));
244}
245
246static FacePersistency
247parseFacePersistency(const std::string& s)
248{
249 if (s == "persistent") {
250 return FacePersistency::FACE_PERSISTENCY_PERSISTENT;
251 }
252 if (s == "permanent") {
253 return FacePersistency::FACE_PERSISTENCY_PERMANENT;
254 }
255 NDN_THROW(std::invalid_argument("unrecognized FacePersistency '" + s + "'"));
256}
257
258std::any
259CommandDefinition::parseValue(ArgValueType valueType, const std::string& token)
260{
261 switch (valueType) {
264 break;
265
267 return parseBoolean(token);
268
270 // boost::lexical_cast<uint64_t> will accept negative number
271 int64_t v = boost::lexical_cast<int64_t>(token);
272 if (v < 0) {
273 NDN_THROW(std::out_of_range("value '" + token + "' is negative"));
274 }
275 return static_cast<uint64_t>(v);
276 }
277
279 return token;
280
282 return parseReportFormat(token);
283
285 return Name(token);
286
288 return ndn::FaceUri(token);
289
291 try {
292 return boost::lexical_cast<uint64_t>(token);
293 }
294 catch (const boost::bad_lexical_cast&) {
295 return ndn::FaceUri(token);
296 }
297
299 return parseFacePersistency(token);
300
302 return boost::lexical_cast<RouteOrigin>(token);
303 }
304
305 NDN_CXX_UNREACHABLE;
306}
307
308} // namespace nfd::tools::nfdc
Contains named command arguments.
CommandArguments parse(const std::vector< std::string > &tokens, size_t start=0) const
Parse a command line.
CommandDefinition & addArg(const std::string &name, ArgValueType valueType, Required isRequired=Required::NO, Positional allowPositional=Positional::NO, const std::string &metavar="")
Declare an argument.
CommandDefinition(std::string_view noun, std::string_view verb)
std::ostream & operator<<(std::ostream &os, ArgValueType vt)
Required
Indicates whether an argument is required.
static FacePersistency parseFacePersistency(const std::string &s)
static std::string getMetavarFromType(ArgValueType vt)
static bool parseBoolean(const std::string &s)
static ReportFormat parseReportFormat(const std::string &s)
NDN_LOG_INIT(nfdc.CommandDefinition)
Positional
Indicates whether an argument can be specified as positional.
ArgValueType
Indicates argument value type.
@ FACE_ID_OR_URI
FaceId or FaceUri.
@ REPORT_FORMAT
Report format 'xml' or 'text'.
@ UNSIGNED
Non-negative integer.
@ NONE
Boolean argument without value.
@ FACE_PERSISTENCY
Face persistency 'persistent' or 'permanent'.