blob: cb25c7c88191483d763f8b40e50dd662448ead76 [file] [log] [blame]
Junxiao Shi64567bb2016-09-04 16:00:27 +00001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
3 * Copyright (c) 2014-2016, 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
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
32namespace nfd {
33namespace tools {
34namespace nfdc {
35
36NDN_LOG_INIT(CommandDefinition);
37
38std::ostream&
39operator<<(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";
46 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";
60 }
61 return os << static_cast<int>(vt);
62}
63
64static std::string
65getMetavarFromType(ArgValueType vt)
66{
67 switch (vt) {
68 case ArgValueType::NONE:
69 return "";
70 case ArgValueType::ANY:
71 return "args";
72 case ArgValueType::UNSIGNED:
73 return "uint";
74 case ArgValueType::STRING:
75 return "str";
76 case ArgValueType::REPORT_FORMAT:
77 return "fmt";
78 case ArgValueType::NAME:
79 return "name";
80 case ArgValueType::FACE_URI:
81 return "uri";
82 case ArgValueType::FACE_ID_OR_URI:
83 return "face";
84 case ArgValueType::FACE_PERSISTENCY:
85 return "persistency";
86 }
87 BOOST_ASSERT(false);
88 return "";
89}
90
91CommandDefinition::CommandDefinition(const std::string& noun, const std::string& verb)
92 : m_noun(noun)
93 , m_verb(verb)
94{
95}
96
97CommandDefinition::~CommandDefinition() = default;
98
99CommandDefinition&
100CommandDefinition::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;
Junxiao Shi737c43c2016-09-14 02:51:44 +0000107 BOOST_VERIFY(isNew);
Junxiao Shi64567bb2016-09-04 16:00:27 +0000108
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
124CommandArguments
125CommandDefinition::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
214static ndn::nfd::FacePersistency
215parseFacePersistency(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
226boost::any
227CommandDefinition::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
244 case ArgValueType::STRING:
245 return token;
246
247 case ArgValueType::REPORT_FORMAT:
248 return parseReportFormat(token);
249
250 case ArgValueType::NAME:
251 return Name(token);
252
253 case ArgValueType::FACE_URI:
254 return ndn::util::FaceUri(token);
255
256 case ArgValueType::FACE_ID_OR_URI:
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
264 case ArgValueType::FACE_PERSISTENCY:
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