blob: 18eee1e4e9682cc6e2a9a6e485f095375bbafaa9 [file] [log] [blame]
Junxiao Shi64567bb2016-09-04 16:00:27 +00001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
Junxiao Shi215fd0b2017-01-28 19:01:04 +00003 * Copyright (c) 2014-2017, 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"
Junxiao Shi64567bb2016-09-04 16:00:27 +000027#include <ndn-cxx/util/logger.hpp>
28
29namespace nfd {
30namespace tools {
31namespace nfdc {
32
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";
43 case ArgValueType::UNSIGNED:
44 return os << "non-negative integer";
45 case ArgValueType::STRING:
46 return os << "string";
47 case ArgValueType::REPORT_FORMAT:
48 return os << "ReportFormat";
49 case ArgValueType::NAME:
50 return os << "Name";
51 case ArgValueType::FACE_URI:
52 return os << "FaceUri";
53 case ArgValueType::FACE_ID_OR_URI:
54 return os << "FaceId or FaceUri";
55 case ArgValueType::FACE_PERSISTENCY:
56 return os << "FacePersistency";
57 }
58 return os << static_cast<int>(vt);
59}
60
61static std::string
62getMetavarFromType(ArgValueType vt)
63{
64 switch (vt) {
65 case ArgValueType::NONE:
66 return "";
67 case ArgValueType::ANY:
68 return "args";
69 case ArgValueType::UNSIGNED:
70 return "uint";
71 case ArgValueType::STRING:
72 return "str";
73 case ArgValueType::REPORT_FORMAT:
74 return "fmt";
75 case ArgValueType::NAME:
76 return "name";
77 case ArgValueType::FACE_URI:
78 return "uri";
79 case ArgValueType::FACE_ID_OR_URI:
80 return "face";
81 case ArgValueType::FACE_PERSISTENCY:
82 return "persistency";
83 }
84 BOOST_ASSERT(false);
85 return "";
86}
87
88CommandDefinition::CommandDefinition(const std::string& noun, const std::string& verb)
89 : m_noun(noun)
90 , m_verb(verb)
91{
92}
93
94CommandDefinition::~CommandDefinition() = default;
95
96CommandDefinition&
97CommandDefinition::addArg(const std::string& name, ArgValueType valueType,
98 Required isRequired, Positional allowPositional,
99 const std::string& metavar)
100{
101 bool isNew = m_args.emplace(name,
102 Arg{name, valueType, static_cast<bool>(isRequired),
103 metavar.empty() ? getMetavarFromType(valueType) : metavar}).second;
Junxiao Shi737c43c2016-09-14 02:51:44 +0000104 BOOST_VERIFY(isNew);
Junxiao Shi64567bb2016-09-04 16:00:27 +0000105
106 if (static_cast<bool>(isRequired)) {
107 m_requiredArgs.insert(name);
108 }
109
110 if (static_cast<bool>(allowPositional)) {
111 BOOST_ASSERT(valueType != ArgValueType::NONE);
112 m_positionalArgs.push_back(name);
113 }
114 else {
115 BOOST_ASSERT(valueType != ArgValueType::ANY);
116 }
117
118 return *this;
119}
120
121CommandArguments
122CommandDefinition::parse(const std::vector<std::string>& tokens, size_t start) const
123{
124 CommandArguments ca;
125
126 size_t positionalArgIndex = 0;
127 for (size_t i = start; i < tokens.size(); ++i) {
128 const std::string& token = tokens[i];
129
130 // try to parse as named argument
131 auto namedArg = m_args.find(token);
132 if (namedArg != m_args.end() && namedArg->second.valueType != ArgValueType::ANY) {
133 NDN_LOG_TRACE(token << " is a named argument");
134 const Arg& arg = namedArg->second;
135 if (arg.valueType == ArgValueType::NONE) {
136 ca[arg.name] = true;
137 NDN_LOG_TRACE(token << " is a boolean argument");
138 }
139 else if (i + 1 >= tokens.size()) {
140 BOOST_THROW_EXCEPTION(Error(arg.name + ": " + arg.metavar + " is missing"));
141 }
142 else {
143 const std::string& valueToken = tokens[++i];
144 NDN_LOG_TRACE(arg.name << " has value " << valueToken);
145 try {
146 ca[arg.name] = this->parseValue(arg.valueType, valueToken);
147 }
148 catch (const std::exception& e) {
149 NDN_LOG_TRACE(valueToken << " cannot be parsed as " << arg.valueType);
150 BOOST_THROW_EXCEPTION(Error(arg.name + ": cannot parse '" + valueToken + "' as " +
151 arg.metavar + " (" + e.what() + ")"));
152 }
153 NDN_LOG_TRACE(valueToken << " is parsed as " << arg.valueType);
154 }
155
156 // disallow positional arguments after named argument
157 positionalArgIndex = m_positionalArgs.size();
158 continue;
159 }
160
161 // try to parse as positional argument
162 for (; positionalArgIndex < m_positionalArgs.size(); ++positionalArgIndex) {
163 const Arg& arg = m_args.at(m_positionalArgs[positionalArgIndex]);
164
165 if (arg.valueType == ArgValueType::ANY) {
166 std::vector<std::string> values;
167 std::copy(tokens.begin() + i, tokens.end(), std::back_inserter(values));
168 ca[arg.name] = values;
169 NDN_LOG_TRACE((tokens.size() - i) << " tokens are consumed for " << arg.name);
170 i = tokens.size();
171 break;
172 }
173
174 try {
175 ca[arg.name] = this->parseValue(arg.valueType, token);
176 NDN_LOG_TRACE(token << " is parsed as value for " << arg.name);
177 break;
178 }
179 catch (const std::exception& e) {
180 if (arg.isRequired) { // the current token must be parsed as the value for arg
181 NDN_LOG_TRACE(token << " cannot be parsed as value for " << arg.name);
182 BOOST_THROW_EXCEPTION(Error("cannot parse '" + token + "' as an argument name or as " +
183 arg.metavar + " for " + arg.name + " (" + e.what() + ")"));
184 }
185 else {
186 // the current token may be a value for next positional argument
187 NDN_LOG_TRACE(token << " cannot be parsed as value for " << arg.name);
188 }
189 }
190 }
191
192 if (positionalArgIndex >= m_positionalArgs.size()) {
193 // for loop has reached the end without finding a match,
194 // which means token is not accepted as a value for positional argument
195 BOOST_THROW_EXCEPTION(Error("cannot parse '" + token + "' as an argument name"));
196 }
197
198 // token is accepted; don't parse as the same positional argument again
199 ++positionalArgIndex;
200 }
201
202 for (const std::string& argName : m_requiredArgs) {
203 if (ca.count(argName) == 0) {
204 BOOST_THROW_EXCEPTION(Error(argName + ": required argument is missing"));
205 }
206 }
207
208 return ca;
209}
210
Junxiao Shi215fd0b2017-01-28 19:01:04 +0000211static FacePersistency
Junxiao Shi64567bb2016-09-04 16:00:27 +0000212parseFacePersistency(const std::string& s)
213{
214 if (s == "persistent") {
Junxiao Shi215fd0b2017-01-28 19:01:04 +0000215 return FacePersistency::FACE_PERSISTENCY_PERSISTENT;
Junxiao Shi64567bb2016-09-04 16:00:27 +0000216 }
217 if (s == "permanent") {
Junxiao Shi215fd0b2017-01-28 19:01:04 +0000218 return FacePersistency::FACE_PERSISTENCY_PERMANENT;
Junxiao Shi64567bb2016-09-04 16:00:27 +0000219 }
220 BOOST_THROW_EXCEPTION(std::invalid_argument("unrecognized FacePersistency"));
221}
222
223boost::any
224CommandDefinition::parseValue(ArgValueType valueType, const std::string& token) const
225{
226 switch (valueType) {
227 case ArgValueType::NONE:
228 case ArgValueType::ANY:
229 BOOST_ASSERT(false);
230 return boost::any();
231
232 case ArgValueType::UNSIGNED: {
233 // boost::lexical_cast<uint64_t> will accept negative number
234 int64_t v = boost::lexical_cast<int64_t>(token);
235 if (v < 0) {
236 BOOST_THROW_EXCEPTION(std::out_of_range("value is negative"));
237 }
238 return static_cast<uint64_t>(v);
239 }
240
241 case ArgValueType::STRING:
242 return token;
243
244 case ArgValueType::REPORT_FORMAT:
245 return parseReportFormat(token);
246
247 case ArgValueType::NAME:
248 return Name(token);
249
250 case ArgValueType::FACE_URI:
Junxiao Shi215fd0b2017-01-28 19:01:04 +0000251 return FaceUri(token);
Junxiao Shi64567bb2016-09-04 16:00:27 +0000252
253 case ArgValueType::FACE_ID_OR_URI:
254 try {
255 return boost::lexical_cast<uint64_t>(token);
256 }
257 catch (const boost::bad_lexical_cast&) {
Junxiao Shi215fd0b2017-01-28 19:01:04 +0000258 return FaceUri(token);
Junxiao Shi64567bb2016-09-04 16:00:27 +0000259 }
260
261 case ArgValueType::FACE_PERSISTENCY:
262 return parseFacePersistency(token);
263 }
264
265 BOOST_ASSERT(false);
266 return boost::any();
267}
268
269} // namespace nfdc
270} // namespace tools
271} // namespace nfd