blob: 08952fd3029fc979e767a8d728b3b8040810b29f [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
Junxiao Shi64567bb2016-09-04 16:00:27 +000029#include <ndn-cxx/util/logger.hpp>
30
Davide Pesaventoe422f9e2022-06-03 01:30:23 -040031namespace nfd::tools::nfdc {
Junxiao Shi64567bb2016-09-04 16:00:27 +000032
Junxiao Shic143afe2016-09-20 13:04:51 +000033NDN_LOG_INIT(nfdc.CommandDefinition);
Junxiao Shi64567bb2016-09-04 16:00:27 +000034
35std::ostream&
36operator<<(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";
Eric Newberry84d3adc2017-08-09 23:31:40 -040043 case ArgValueType::BOOLEAN:
44 return os << "boolean";
Junxiao Shi64567bb2016-09-04 16:00:27 +000045 case ArgValueType::UNSIGNED:
46 return os << "non-negative integer";
47 case ArgValueType::STRING:
48 return os << "string";
49 case ArgValueType::REPORT_FORMAT:
50 return os << "ReportFormat";
51 case ArgValueType::NAME:
52 return os << "Name";
53 case ArgValueType::FACE_URI:
54 return os << "FaceUri";
55 case ArgValueType::FACE_ID_OR_URI:
56 return os << "FaceId or FaceUri";
57 case ArgValueType::FACE_PERSISTENCY:
58 return os << "FacePersistency";
Junxiao Shi8eda6822017-04-12 02:53:14 +000059 case ArgValueType::ROUTE_ORIGIN:
60 return os << "RouteOrigin";
Junxiao Shi64567bb2016-09-04 16:00:27 +000061 }
62 return os << static_cast<int>(vt);
63}
64
65static std::string
66getMetavarFromType(ArgValueType vt)
67{
68 switch (vt) {
69 case ArgValueType::NONE:
70 return "";
71 case ArgValueType::ANY:
72 return "args";
Eric Newberry84d3adc2017-08-09 23:31:40 -040073 case ArgValueType::BOOLEAN:
74 return "bool";
Junxiao Shi64567bb2016-09-04 16:00:27 +000075 case ArgValueType::UNSIGNED:
76 return "uint";
77 case ArgValueType::STRING:
78 return "str";
79 case ArgValueType::REPORT_FORMAT:
80 return "fmt";
81 case ArgValueType::NAME:
82 return "name";
83 case ArgValueType::FACE_URI:
84 return "uri";
85 case ArgValueType::FACE_ID_OR_URI:
86 return "face";
87 case ArgValueType::FACE_PERSISTENCY:
88 return "persistency";
Junxiao Shi8eda6822017-04-12 02:53:14 +000089 case ArgValueType::ROUTE_ORIGIN:
90 return "origin";
Junxiao Shi64567bb2016-09-04 16:00:27 +000091 }
Davide Pesavento5a897692019-10-31 01:28:43 -040092 NDN_CXX_UNREACHABLE;
Junxiao Shi64567bb2016-09-04 16:00:27 +000093}
94
Davide Pesaventoa3a7a4e2022-05-29 16:06:22 -040095CommandDefinition::CommandDefinition(std::string_view noun, std::string_view verb)
Junxiao Shi64567bb2016-09-04 16:00:27 +000096 : m_noun(noun)
97 , m_verb(verb)
98{
99}
100
101CommandDefinition::~CommandDefinition() = default;
102
103CommandDefinition&
104CommandDefinition::addArg(const std::string& name, ArgValueType valueType,
Davide Pesaventoa3a7a4e2022-05-29 16:06:22 -0400105 Required isRequired, Positional allowPositional,
106 const std::string& metavar)
Junxiao Shi64567bb2016-09-04 16:00:27 +0000107{
108 bool isNew = m_args.emplace(name,
109 Arg{name, valueType, static_cast<bool>(isRequired),
110 metavar.empty() ? getMetavarFromType(valueType) : metavar}).second;
Junxiao Shi737c43c2016-09-14 02:51:44 +0000111 BOOST_VERIFY(isNew);
Junxiao Shi64567bb2016-09-04 16:00:27 +0000112
113 if (static_cast<bool>(isRequired)) {
114 m_requiredArgs.insert(name);
115 }
116
117 if (static_cast<bool>(allowPositional)) {
118 BOOST_ASSERT(valueType != ArgValueType::NONE);
119 m_positionalArgs.push_back(name);
120 }
121 else {
122 BOOST_ASSERT(valueType != ArgValueType::ANY);
123 }
124
125 return *this;
126}
127
128CommandArguments
129CommandDefinition::parse(const std::vector<std::string>& tokens, size_t start) const
130{
131 CommandArguments ca;
132
133 size_t positionalArgIndex = 0;
134 for (size_t i = start; i < tokens.size(); ++i) {
135 const std::string& token = tokens[i];
136
137 // try to parse as named argument
138 auto namedArg = m_args.find(token);
139 if (namedArg != m_args.end() && namedArg->second.valueType != ArgValueType::ANY) {
140 NDN_LOG_TRACE(token << " is a named argument");
141 const Arg& arg = namedArg->second;
142 if (arg.valueType == ArgValueType::NONE) {
143 ca[arg.name] = true;
Eric Newberry84d3adc2017-08-09 23:31:40 -0400144 NDN_LOG_TRACE(token << " is a no-param argument");
Junxiao Shi64567bb2016-09-04 16:00:27 +0000145 }
146 else if (i + 1 >= tokens.size()) {
Davide Pesavento19779d82019-02-14 13:40:04 -0500147 NDN_THROW(Error(arg.name + ": " + arg.metavar + " is missing"));
Junxiao Shi64567bb2016-09-04 16:00:27 +0000148 }
149 else {
150 const std::string& valueToken = tokens[++i];
151 NDN_LOG_TRACE(arg.name << " has value " << valueToken);
152 try {
Davide Pesaventoa3a7a4e2022-05-29 16:06:22 -0400153 ca[arg.name] = parseValue(arg.valueType, valueToken);
Junxiao Shi64567bb2016-09-04 16:00:27 +0000154 }
155 catch (const std::exception& e) {
156 NDN_LOG_TRACE(valueToken << " cannot be parsed as " << arg.valueType);
Davide Pesavento19779d82019-02-14 13:40:04 -0500157 NDN_THROW_NESTED(Error(arg.name + ": cannot parse '" + valueToken + "' as " +
158 arg.metavar + " (" + e.what() + ")"));
Junxiao Shi64567bb2016-09-04 16:00:27 +0000159 }
160 NDN_LOG_TRACE(valueToken << " is parsed as " << arg.valueType);
161 }
162
163 // disallow positional arguments after named argument
164 positionalArgIndex = m_positionalArgs.size();
165 continue;
166 }
167
168 // try to parse as positional argument
169 for (; positionalArgIndex < m_positionalArgs.size(); ++positionalArgIndex) {
170 const Arg& arg = m_args.at(m_positionalArgs[positionalArgIndex]);
171
172 if (arg.valueType == ArgValueType::ANY) {
173 std::vector<std::string> values;
174 std::copy(tokens.begin() + i, tokens.end(), std::back_inserter(values));
175 ca[arg.name] = values;
176 NDN_LOG_TRACE((tokens.size() - i) << " tokens are consumed for " << arg.name);
177 i = tokens.size();
178 break;
179 }
180
181 try {
Davide Pesaventoa3a7a4e2022-05-29 16:06:22 -0400182 ca[arg.name] = parseValue(arg.valueType, token);
Junxiao Shi64567bb2016-09-04 16:00:27 +0000183 NDN_LOG_TRACE(token << " is parsed as value for " << arg.name);
184 break;
185 }
186 catch (const std::exception& e) {
187 if (arg.isRequired) { // the current token must be parsed as the value for arg
188 NDN_LOG_TRACE(token << " cannot be parsed as value for " << arg.name);
Davide Pesavento19779d82019-02-14 13:40:04 -0500189 NDN_THROW_NESTED(Error("cannot parse '" + token + "' as an argument name or as " +
190 arg.metavar + " for " + arg.name + " (" + e.what() + ")"));
Junxiao Shi64567bb2016-09-04 16:00:27 +0000191 }
192 else {
193 // the current token may be a value for next positional argument
194 NDN_LOG_TRACE(token << " cannot be parsed as value for " << arg.name);
195 }
196 }
197 }
198
199 if (positionalArgIndex >= m_positionalArgs.size()) {
200 // for loop has reached the end without finding a match,
201 // which means token is not accepted as a value for positional argument
Davide Pesavento19779d82019-02-14 13:40:04 -0500202 NDN_THROW(Error("cannot parse '" + token + "' as an argument name"));
Junxiao Shi64567bb2016-09-04 16:00:27 +0000203 }
204
205 // token is accepted; don't parse as the same positional argument again
206 ++positionalArgIndex;
207 }
208
Davide Pesaventoa3a7a4e2022-05-29 16:06:22 -0400209 for (const auto& argName : m_requiredArgs) {
Junxiao Shi64567bb2016-09-04 16:00:27 +0000210 if (ca.count(argName) == 0) {
Davide Pesavento19779d82019-02-14 13:40:04 -0500211 NDN_THROW(Error(argName + ": required argument is missing"));
Junxiao Shi64567bb2016-09-04 16:00:27 +0000212 }
213 }
214
215 return ca;
216}
217
Eric Newberry84d3adc2017-08-09 23:31:40 -0400218static bool
219parseBoolean(const std::string& s)
220{
221 if (s == "on" || s == "true" || s == "enabled" || s == "yes" || s == "1") {
222 return true;
223 }
224 if (s == "off" || s == "false" || s == "disabled" || s == "no" || s == "0") {
225 return false;
226 }
Davide Pesavento19779d82019-02-14 13:40:04 -0500227 NDN_THROW(std::invalid_argument("unrecognized boolean value '" + s + "'"));
Eric Newberry84d3adc2017-08-09 23:31:40 -0400228}
229
Junxiao Shi215fd0b2017-01-28 19:01:04 +0000230static FacePersistency
Junxiao Shi64567bb2016-09-04 16:00:27 +0000231parseFacePersistency(const std::string& s)
232{
233 if (s == "persistent") {
Junxiao Shi215fd0b2017-01-28 19:01:04 +0000234 return FacePersistency::FACE_PERSISTENCY_PERSISTENT;
Junxiao Shi64567bb2016-09-04 16:00:27 +0000235 }
236 if (s == "permanent") {
Junxiao Shi215fd0b2017-01-28 19:01:04 +0000237 return FacePersistency::FACE_PERSISTENCY_PERMANENT;
Junxiao Shi64567bb2016-09-04 16:00:27 +0000238 }
Davide Pesavento19779d82019-02-14 13:40:04 -0500239 NDN_THROW(std::invalid_argument("unrecognized FacePersistency '" + s + "'"));
Junxiao Shi64567bb2016-09-04 16:00:27 +0000240}
241
Davide Pesaventob7bfcb92022-05-22 23:55:23 -0400242std::any
Davide Pesaventoa3a7a4e2022-05-29 16:06:22 -0400243CommandDefinition::parseValue(ArgValueType valueType, const std::string& token)
Junxiao Shi64567bb2016-09-04 16:00:27 +0000244{
245 switch (valueType) {
246 case ArgValueType::NONE:
247 case ArgValueType::ANY:
Davide Pesavento5a897692019-10-31 01:28:43 -0400248 break;
Junxiao Shi64567bb2016-09-04 16:00:27 +0000249
Davide Pesavento8b663a92018-11-21 22:57:20 -0500250 case ArgValueType::BOOLEAN:
Eric Newberry84d3adc2017-08-09 23:31:40 -0400251 return parseBoolean(token);
Eric Newberry84d3adc2017-08-09 23:31:40 -0400252
Junxiao Shi64567bb2016-09-04 16:00:27 +0000253 case ArgValueType::UNSIGNED: {
254 // boost::lexical_cast<uint64_t> will accept negative number
255 int64_t v = boost::lexical_cast<int64_t>(token);
256 if (v < 0) {
Davide Pesavento19779d82019-02-14 13:40:04 -0500257 NDN_THROW(std::out_of_range("value '" + token + "' is negative"));
Junxiao Shi64567bb2016-09-04 16:00:27 +0000258 }
259 return static_cast<uint64_t>(v);
260 }
261
262 case ArgValueType::STRING:
263 return token;
264
265 case ArgValueType::REPORT_FORMAT:
266 return parseReportFormat(token);
267
268 case ArgValueType::NAME:
269 return Name(token);
270
271 case ArgValueType::FACE_URI:
Junxiao Shi215fd0b2017-01-28 19:01:04 +0000272 return FaceUri(token);
Junxiao Shi64567bb2016-09-04 16:00:27 +0000273
274 case ArgValueType::FACE_ID_OR_URI:
275 try {
276 return boost::lexical_cast<uint64_t>(token);
277 }
278 catch (const boost::bad_lexical_cast&) {
Junxiao Shi215fd0b2017-01-28 19:01:04 +0000279 return FaceUri(token);
Junxiao Shi64567bb2016-09-04 16:00:27 +0000280 }
281
282 case ArgValueType::FACE_PERSISTENCY:
283 return parseFacePersistency(token);
Junxiao Shi8eda6822017-04-12 02:53:14 +0000284
285 case ArgValueType::ROUTE_ORIGIN:
286 return boost::lexical_cast<RouteOrigin>(token);
Junxiao Shi64567bb2016-09-04 16:00:27 +0000287 }
288
Davide Pesavento5a897692019-10-31 01:28:43 -0400289 NDN_CXX_UNREACHABLE;
Junxiao Shi64567bb2016-09-04 16:00:27 +0000290}
291
Davide Pesaventoe422f9e2022-06-03 01:30:23 -0400292} // namespace nfd::tools::nfdc