/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /* * Copyright (c) 2014-2019, Regents of the University of California, * Arizona Board of Regents, * Colorado State University, * University Pierre & Marie Curie, Sorbonne University, * Washington University in St. Louis, * Beijing Institute of Technology, * The University of Memphis. * * This file is part of NFD (Named Data Networking Forwarding Daemon). * See AUTHORS.md for complete list of NFD authors and contributors. * * NFD is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Foundation, * either version 3 of the License, or (at your option) any later version. * * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * NFD, e.g., in COPYING.md file. If not, see . */ #include "command-definition.hpp" #include namespace nfd { namespace tools { namespace nfdc { NDN_LOG_INIT(nfdc.CommandDefinition); std::ostream& operator<<(std::ostream& os, ArgValueType vt) { switch (vt) { case ArgValueType::NONE: return os << "none"; case ArgValueType::ANY: return os << "any"; case ArgValueType::BOOLEAN: return os << "boolean"; case ArgValueType::UNSIGNED: return os << "non-negative integer"; case ArgValueType::STRING: return os << "string"; case ArgValueType::REPORT_FORMAT: return os << "ReportFormat"; case ArgValueType::NAME: return os << "Name"; case ArgValueType::FACE_URI: return os << "FaceUri"; case ArgValueType::FACE_ID_OR_URI: return os << "FaceId or FaceUri"; case ArgValueType::FACE_PERSISTENCY: return os << "FacePersistency"; case ArgValueType::ROUTE_ORIGIN: return os << "RouteOrigin"; } return os << static_cast(vt); } static std::string getMetavarFromType(ArgValueType vt) { switch (vt) { case ArgValueType::NONE: return ""; case ArgValueType::ANY: return "args"; case ArgValueType::BOOLEAN: return "bool"; case ArgValueType::UNSIGNED: return "uint"; case ArgValueType::STRING: return "str"; case ArgValueType::REPORT_FORMAT: return "fmt"; case ArgValueType::NAME: return "name"; case ArgValueType::FACE_URI: return "uri"; case ArgValueType::FACE_ID_OR_URI: return "face"; case ArgValueType::FACE_PERSISTENCY: return "persistency"; case ArgValueType::ROUTE_ORIGIN: return "origin"; } BOOST_ASSERT(false); return ""; } CommandDefinition::CommandDefinition(const std::string& noun, const std::string& verb) : m_noun(noun) , m_verb(verb) { } CommandDefinition::~CommandDefinition() = default; CommandDefinition& CommandDefinition::addArg(const std::string& name, ArgValueType valueType, Required isRequired, Positional allowPositional, const std::string& metavar) { bool isNew = m_args.emplace(name, Arg{name, valueType, static_cast(isRequired), metavar.empty() ? getMetavarFromType(valueType) : metavar}).second; BOOST_VERIFY(isNew); if (static_cast(isRequired)) { m_requiredArgs.insert(name); } if (static_cast(allowPositional)) { BOOST_ASSERT(valueType != ArgValueType::NONE); m_positionalArgs.push_back(name); } else { BOOST_ASSERT(valueType != ArgValueType::ANY); } return *this; } CommandArguments CommandDefinition::parse(const std::vector& tokens, size_t start) const { CommandArguments ca; size_t positionalArgIndex = 0; for (size_t i = start; i < tokens.size(); ++i) { const std::string& token = tokens[i]; // try to parse as named argument auto namedArg = m_args.find(token); if (namedArg != m_args.end() && namedArg->second.valueType != ArgValueType::ANY) { NDN_LOG_TRACE(token << " is a named argument"); const Arg& arg = namedArg->second; if (arg.valueType == ArgValueType::NONE) { ca[arg.name] = true; NDN_LOG_TRACE(token << " is a no-param argument"); } else if (i + 1 >= tokens.size()) { NDN_THROW(Error(arg.name + ": " + arg.metavar + " is missing")); } else { const std::string& valueToken = tokens[++i]; NDN_LOG_TRACE(arg.name << " has value " << valueToken); try { ca[arg.name] = this->parseValue(arg.valueType, valueToken); } catch (const std::exception& e) { NDN_LOG_TRACE(valueToken << " cannot be parsed as " << arg.valueType); NDN_THROW_NESTED(Error(arg.name + ": cannot parse '" + valueToken + "' as " + arg.metavar + " (" + e.what() + ")")); } NDN_LOG_TRACE(valueToken << " is parsed as " << arg.valueType); } // disallow positional arguments after named argument positionalArgIndex = m_positionalArgs.size(); continue; } // try to parse as positional argument for (; positionalArgIndex < m_positionalArgs.size(); ++positionalArgIndex) { const Arg& arg = m_args.at(m_positionalArgs[positionalArgIndex]); if (arg.valueType == ArgValueType::ANY) { std::vector values; std::copy(tokens.begin() + i, tokens.end(), std::back_inserter(values)); ca[arg.name] = values; NDN_LOG_TRACE((tokens.size() - i) << " tokens are consumed for " << arg.name); i = tokens.size(); break; } try { ca[arg.name] = this->parseValue(arg.valueType, token); NDN_LOG_TRACE(token << " is parsed as value for " << arg.name); break; } catch (const std::exception& e) { if (arg.isRequired) { // the current token must be parsed as the value for arg NDN_LOG_TRACE(token << " cannot be parsed as value for " << arg.name); NDN_THROW_NESTED(Error("cannot parse '" + token + "' as an argument name or as " + arg.metavar + " for " + arg.name + " (" + e.what() + ")")); } else { // the current token may be a value for next positional argument NDN_LOG_TRACE(token << " cannot be parsed as value for " << arg.name); } } } if (positionalArgIndex >= m_positionalArgs.size()) { // for loop has reached the end without finding a match, // which means token is not accepted as a value for positional argument NDN_THROW(Error("cannot parse '" + token + "' as an argument name")); } // token is accepted; don't parse as the same positional argument again ++positionalArgIndex; } for (const std::string& argName : m_requiredArgs) { if (ca.count(argName) == 0) { NDN_THROW(Error(argName + ": required argument is missing")); } } return ca; } static bool parseBoolean(const std::string& s) { if (s == "on" || s == "true" || s == "enabled" || s == "yes" || s == "1") { return true; } if (s == "off" || s == "false" || s == "disabled" || s == "no" || s == "0") { return false; } NDN_THROW(std::invalid_argument("unrecognized boolean value '" + s + "'")); } static FacePersistency parseFacePersistency(const std::string& s) { if (s == "persistent") { return FacePersistency::FACE_PERSISTENCY_PERSISTENT; } if (s == "permanent") { return FacePersistency::FACE_PERSISTENCY_PERMANENT; } NDN_THROW(std::invalid_argument("unrecognized FacePersistency '" + s + "'")); } ndn::any CommandDefinition::parseValue(ArgValueType valueType, const std::string& token) const { switch (valueType) { case ArgValueType::NONE: case ArgValueType::ANY: BOOST_ASSERT(false); return {}; case ArgValueType::BOOLEAN: return parseBoolean(token); case ArgValueType::UNSIGNED: { // boost::lexical_cast will accept negative number int64_t v = boost::lexical_cast(token); if (v < 0) { NDN_THROW(std::out_of_range("value '" + token + "' is negative")); } return static_cast(v); } case ArgValueType::STRING: return token; case ArgValueType::REPORT_FORMAT: return parseReportFormat(token); case ArgValueType::NAME: return Name(token); case ArgValueType::FACE_URI: return FaceUri(token); case ArgValueType::FACE_ID_OR_URI: try { return boost::lexical_cast(token); } catch (const boost::bad_lexical_cast&) { return FaceUri(token); } case ArgValueType::FACE_PERSISTENCY: return parseFacePersistency(token); case ArgValueType::ROUTE_ORIGIN: return boost::lexical_cast(token); } BOOST_ASSERT(false); return {}; } } // namespace nfdc } // namespace tools } // namespace nfd