blob: 443aff032ae6df1d6886abcd9d0807b837f6f611 [file] [log] [blame]
Junxiao Shi64567bb2016-09-04 16:00:27 +00001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Eric Newberry84d3adc2017-08-09 23:31:40 -04002/*
Davide Pesaventob7bfcb92022-05-22 23:55:23 -04003 * Copyright (c) 2014-2022, Regents of the University of California,
Junxiao Shi64567bb2016-09-04 16:00:27 +00004 * 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"
Davide Pesaventoa3a7a4e2022-05-29 16:06:22 -040027#include "status-report.hpp"
Davide Pesavento8b663a92018-11-21 22:57:20 -050028
Davide Pesaventoa9b09b62022-06-04 14:07:25 -040029#include <boost/lexical_cast.hpp>
Junxiao Shi64567bb2016-09-04 16:00:27 +000030#include <ndn-cxx/util/logger.hpp>
31
Davide Pesaventoe422f9e2022-06-03 01:30:23 -040032namespace nfd::tools::nfdc {
Junxiao Shi64567bb2016-09-04 16:00:27 +000033
Junxiao Shic143afe2016-09-20 13:04:51 +000034NDN_LOG_INIT(nfdc.CommandDefinition);
Junxiao Shi64567bb2016-09-04 16:00:27 +000035
36std::ostream&
37operator<<(std::ostream& os, ArgValueType vt)
38{
39 switch (vt) {
40 case ArgValueType::NONE:
41 return os << "none";
42 case ArgValueType::ANY:
43 return os << "any";
Eric Newberry84d3adc2017-08-09 23:31:40 -040044 case ArgValueType::BOOLEAN:
45 return os << "boolean";
Junxiao Shi64567bb2016-09-04 16:00:27 +000046 case ArgValueType::UNSIGNED:
47 return os << "non-negative integer";
48 case ArgValueType::STRING:
49 return os << "string";
50 case ArgValueType::REPORT_FORMAT:
51 return os << "ReportFormat";
52 case ArgValueType::NAME:
53 return os << "Name";
54 case ArgValueType::FACE_URI:
55 return os << "FaceUri";
56 case ArgValueType::FACE_ID_OR_URI:
57 return os << "FaceId or FaceUri";
58 case ArgValueType::FACE_PERSISTENCY:
59 return os << "FacePersistency";
Junxiao Shi8eda6822017-04-12 02:53:14 +000060 case ArgValueType::ROUTE_ORIGIN:
61 return os << "RouteOrigin";
Junxiao Shi64567bb2016-09-04 16:00:27 +000062 }
63 return os << static_cast<int>(vt);
64}
65
66static std::string
67getMetavarFromType(ArgValueType vt)
68{
69 switch (vt) {
70 case ArgValueType::NONE:
71 return "";
72 case ArgValueType::ANY:
73 return "args";
Eric Newberry84d3adc2017-08-09 23:31:40 -040074 case ArgValueType::BOOLEAN:
75 return "bool";
Junxiao Shi64567bb2016-09-04 16:00:27 +000076 case ArgValueType::UNSIGNED:
77 return "uint";
78 case ArgValueType::STRING:
79 return "str";
80 case ArgValueType::REPORT_FORMAT:
81 return "fmt";
82 case ArgValueType::NAME:
83 return "name";
84 case ArgValueType::FACE_URI:
85 return "uri";
86 case ArgValueType::FACE_ID_OR_URI:
87 return "face";
88 case ArgValueType::FACE_PERSISTENCY:
89 return "persistency";
Junxiao Shi8eda6822017-04-12 02:53:14 +000090 case ArgValueType::ROUTE_ORIGIN:
91 return "origin";
Junxiao Shi64567bb2016-09-04 16:00:27 +000092 }
Davide Pesavento5a897692019-10-31 01:28:43 -040093 NDN_CXX_UNREACHABLE;
Junxiao Shi64567bb2016-09-04 16:00:27 +000094}
95
Davide Pesaventoa3a7a4e2022-05-29 16:06:22 -040096CommandDefinition::CommandDefinition(std::string_view noun, std::string_view verb)
Junxiao Shi64567bb2016-09-04 16:00:27 +000097 : m_noun(noun)
98 , m_verb(verb)
99{
100}
101
102CommandDefinition::~CommandDefinition() = default;
103
104CommandDefinition&
105CommandDefinition::addArg(const std::string& name, ArgValueType valueType,
Davide Pesaventoa3a7a4e2022-05-29 16:06:22 -0400106 Required isRequired, Positional allowPositional,
107 const std::string& metavar)
Junxiao Shi64567bb2016-09-04 16:00:27 +0000108{
109 bool isNew = m_args.emplace(name,
110 Arg{name, valueType, static_cast<bool>(isRequired),
111 metavar.empty() ? getMetavarFromType(valueType) : metavar}).second;
Junxiao Shi737c43c2016-09-14 02:51:44 +0000112 BOOST_VERIFY(isNew);
Junxiao Shi64567bb2016-09-04 16:00:27 +0000113
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
129CommandArguments
130CommandDefinition::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;
Eric Newberry84d3adc2017-08-09 23:31:40 -0400145 NDN_LOG_TRACE(token << " is a no-param argument");
Junxiao Shi64567bb2016-09-04 16:00:27 +0000146 }
147 else if (i + 1 >= tokens.size()) {
Davide Pesavento19779d82019-02-14 13:40:04 -0500148 NDN_THROW(Error(arg.name + ": " + arg.metavar + " is missing"));
Junxiao Shi64567bb2016-09-04 16:00:27 +0000149 }
150 else {
151 const std::string& valueToken = tokens[++i];
152 NDN_LOG_TRACE(arg.name << " has value " << valueToken);
153 try {
Davide Pesaventoa3a7a4e2022-05-29 16:06:22 -0400154 ca[arg.name] = parseValue(arg.valueType, valueToken);
Junxiao Shi64567bb2016-09-04 16:00:27 +0000155 }
156 catch (const std::exception& e) {
157 NDN_LOG_TRACE(valueToken << " cannot be parsed as " << arg.valueType);
Davide Pesavento19779d82019-02-14 13:40:04 -0500158 NDN_THROW_NESTED(Error(arg.name + ": cannot parse '" + valueToken + "' as " +
159 arg.metavar + " (" + e.what() + ")"));
Junxiao Shi64567bb2016-09-04 16:00:27 +0000160 }
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 {
Davide Pesaventoa3a7a4e2022-05-29 16:06:22 -0400183 ca[arg.name] = parseValue(arg.valueType, token);
Junxiao Shi64567bb2016-09-04 16:00:27 +0000184 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);
Davide Pesavento19779d82019-02-14 13:40:04 -0500190 NDN_THROW_NESTED(Error("cannot parse '" + token + "' as an argument name or as " +
191 arg.metavar + " for " + arg.name + " (" + e.what() + ")"));
Junxiao Shi64567bb2016-09-04 16:00:27 +0000192 }
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
Davide Pesavento19779d82019-02-14 13:40:04 -0500203 NDN_THROW(Error("cannot parse '" + token + "' as an argument name"));
Junxiao Shi64567bb2016-09-04 16:00:27 +0000204 }
205
206 // token is accepted; don't parse as the same positional argument again
207 ++positionalArgIndex;
208 }
209
Davide Pesaventoa3a7a4e2022-05-29 16:06:22 -0400210 for (const auto& argName : m_requiredArgs) {
Junxiao Shi64567bb2016-09-04 16:00:27 +0000211 if (ca.count(argName) == 0) {
Davide Pesavento19779d82019-02-14 13:40:04 -0500212 NDN_THROW(Error(argName + ": required argument is missing"));
Junxiao Shi64567bb2016-09-04 16:00:27 +0000213 }
214 }
215
216 return ca;
217}
218
Eric Newberry84d3adc2017-08-09 23:31:40 -0400219static bool
220parseBoolean(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 }
Davide Pesavento19779d82019-02-14 13:40:04 -0500228 NDN_THROW(std::invalid_argument("unrecognized boolean value '" + s + "'"));
Eric Newberry84d3adc2017-08-09 23:31:40 -0400229}
230
Davide Pesavento990620a2022-06-05 00:25:53 -0400231static ReportFormat
232parseReportFormat(const std::string& s)
233{
234 if (s == "xml") {
235 return ReportFormat::XML;
236 }
237 if (s == "text") {
238 return ReportFormat::TEXT;
239 }
240 NDN_THROW(std::invalid_argument("unrecognized ReportFormat '" + s + "'"));
241}
242
Junxiao Shi215fd0b2017-01-28 19:01:04 +0000243static FacePersistency
Junxiao Shi64567bb2016-09-04 16:00:27 +0000244parseFacePersistency(const std::string& s)
245{
246 if (s == "persistent") {
Junxiao Shi215fd0b2017-01-28 19:01:04 +0000247 return FacePersistency::FACE_PERSISTENCY_PERSISTENT;
Junxiao Shi64567bb2016-09-04 16:00:27 +0000248 }
249 if (s == "permanent") {
Junxiao Shi215fd0b2017-01-28 19:01:04 +0000250 return FacePersistency::FACE_PERSISTENCY_PERMANENT;
Junxiao Shi64567bb2016-09-04 16:00:27 +0000251 }
Davide Pesavento19779d82019-02-14 13:40:04 -0500252 NDN_THROW(std::invalid_argument("unrecognized FacePersistency '" + s + "'"));
Junxiao Shi64567bb2016-09-04 16:00:27 +0000253}
254
Davide Pesaventob7bfcb92022-05-22 23:55:23 -0400255std::any
Davide Pesaventoa3a7a4e2022-05-29 16:06:22 -0400256CommandDefinition::parseValue(ArgValueType valueType, const std::string& token)
Junxiao Shi64567bb2016-09-04 16:00:27 +0000257{
258 switch (valueType) {
259 case ArgValueType::NONE:
260 case ArgValueType::ANY:
Davide Pesavento5a897692019-10-31 01:28:43 -0400261 break;
Junxiao Shi64567bb2016-09-04 16:00:27 +0000262
Davide Pesavento8b663a92018-11-21 22:57:20 -0500263 case ArgValueType::BOOLEAN:
Eric Newberry84d3adc2017-08-09 23:31:40 -0400264 return parseBoolean(token);
Eric Newberry84d3adc2017-08-09 23:31:40 -0400265
Junxiao Shi64567bb2016-09-04 16:00:27 +0000266 case ArgValueType::UNSIGNED: {
267 // boost::lexical_cast<uint64_t> will accept negative number
268 int64_t v = boost::lexical_cast<int64_t>(token);
269 if (v < 0) {
Davide Pesavento19779d82019-02-14 13:40:04 -0500270 NDN_THROW(std::out_of_range("value '" + token + "' is negative"));
Junxiao Shi64567bb2016-09-04 16:00:27 +0000271 }
272 return static_cast<uint64_t>(v);
273 }
274
275 case ArgValueType::STRING:
276 return token;
277
278 case ArgValueType::REPORT_FORMAT:
279 return parseReportFormat(token);
280
281 case ArgValueType::NAME:
282 return Name(token);
283
284 case ArgValueType::FACE_URI:
Junxiao Shi215fd0b2017-01-28 19:01:04 +0000285 return FaceUri(token);
Junxiao Shi64567bb2016-09-04 16:00:27 +0000286
287 case ArgValueType::FACE_ID_OR_URI:
288 try {
289 return boost::lexical_cast<uint64_t>(token);
290 }
291 catch (const boost::bad_lexical_cast&) {
Junxiao Shi215fd0b2017-01-28 19:01:04 +0000292 return FaceUri(token);
Junxiao Shi64567bb2016-09-04 16:00:27 +0000293 }
294
295 case ArgValueType::FACE_PERSISTENCY:
296 return parseFacePersistency(token);
Junxiao Shi8eda6822017-04-12 02:53:14 +0000297
298 case ArgValueType::ROUTE_ORIGIN:
299 return boost::lexical_cast<RouteOrigin>(token);
Junxiao Shi64567bb2016-09-04 16:00:27 +0000300 }
301
Davide Pesavento5a897692019-10-31 01:28:43 -0400302 NDN_CXX_UNREACHABLE;
Junxiao Shi64567bb2016-09-04 16:00:27 +0000303}
304
Davide Pesaventoe422f9e2022-06-03 01:30:23 -0400305} // namespace nfd::tools::nfdc