/* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef FRAMEWORK_NATIVE_CMDS_IDLCLI_UTILS_H_ #define FRAMEWORK_NATIVE_CMDS_IDLCLI_UTILS_H_ #include #include #include #include #include #include #include #include namespace android { namespace idlcli { namespace overrides { namespace details { template inline std::istream &operator>>(std::istream &stream, T &out) { auto pos = stream.tellg(); auto tmp = +out; auto min = +std::numeric_limits::min(); auto max = +std::numeric_limits::max(); stream >> tmp; if (!stream) { return stream; } if (tmp < min || tmp > max) { stream.seekg(pos); stream.setstate(std::ios_base::failbit); return stream; } out = tmp; return stream; } } // namespace details // override for default behavior of treating as a character inline std::istream &operator>>(std::istream &stream, int8_t &out) { return details::operator>>(stream, out); } // override for default behavior of treating as a character inline std::istream &operator>>(std::istream &stream, uint8_t &out) { return details::operator>>(stream, out); } } // namespace overrides template > inline std::istream &operator>>(std::istream &stream, T &out) { using overrides::operator>>; auto validRange = R(); auto pos = stream.tellg(); std::underlying_type_t in; T tmp; stream >> in; if (!stream) { return stream; } tmp = static_cast(in); if (tmp < *validRange.begin() || tmp > *std::prev(validRange.end())) { stream.seekg(pos); stream.setstate(std::ios_base::failbit); return stream; } out = tmp; return stream; } enum Status : unsigned int { OK, USAGE, UNAVAILABLE, ERROR, }; class Args { public: Args(const int argc, const char *const argv[]) { for (int argi = 0; argi < argc; argi++) { mArgs.emplace_back(std::string_view(argv[argi])); } } template std::optional get() { return get(false); } template std::optional pop() { return get(true); } bool empty() { return mArgs.empty(); } private: template std::optional get(bool erase) { using idlcli::operator>>; using overrides::operator>>; T retValue; if (mArgs.empty()) { return {}; } std::stringstream stream{std::string{mArgs.front()}}; stream >> std::setbase(0) >> retValue; if (!stream || !stream.eof()) { return {}; } if (erase) { mArgs.erase(mArgs.begin()); } return retValue; } std::vector mArgs; }; class Command { protected: struct Usage { std::string name; std::vector details; }; using UsageDetails = std::vector; public: virtual ~Command() = default; Status main(Args &&args) { Status status = doArgsAndMain(std::move(args)); if (status == USAGE) { printUsage(); return ERROR; } if (status == UNAVAILABLE) { std::cerr << "The requested operation is unavailable." << std::endl; return ERROR; } return status; } private: virtual std::string getDescription() const = 0; virtual std::string getUsageSummary() const = 0; virtual UsageDetails getUsageDetails() const = 0; virtual Status doArgs(Args &args) = 0; virtual Status doMain(Args &&args) = 0; void printUsage() const { std::cerr << "Description:\n " << getDescription() << std::endl; std::cerr << "Usage:\n " << mName << " " << getUsageSummary() << std::endl; std::cerr << "Details:" << std::endl; size_t entryNameWidth = 0; for (auto &entry : getUsageDetails()) { entryNameWidth = std::max(entryNameWidth, entry.name.length()); } for (auto &entry : getUsageDetails()) { auto prefix = entry.name; for (auto &line : entry.details) { std::cerr << " " << std::left << std::setw(entryNameWidth + 8) << prefix << line << std::endl; prefix = ""; } } } Status doArgsAndMain(Args &&args) { Status status; mName = *args.pop(); if ((status = doArgs(args)) != OK) { return status; } if ((status = doMain(std::move(args))) != OK) { return status; } return OK; } protected: std::string mName; }; template class CommandRegistry { private: using CommandCreator = std::function()>; public: template static CommandCreator Register(const std::string name) { Instance()->mCommands[name] = [] { return std::make_unique(); }; return Instance()->mCommands[name]; } static std::unique_ptr Create(const std::string name) { auto it = Instance()->mCommands.find(name); if (it == Instance()->mCommands.end()) { return nullptr; } return it->second(); } static auto List() { std::vector list; for (auto &it : Instance()->mCommands) { list.push_back(it.first); } std::sort(list.begin(), list.end()); return list; } private: static CommandRegistry *Instance() { static CommandRegistry sRegistry; return &sRegistry; } private: std::map mCommands; }; template class CommandWithSubcommands : public Command { protected: Status doArgs(Args &args) override { mCommand = CommandRegistry::Create(*args.get()); if (!mCommand) { std::cerr << "Invalid Command!" << std::endl; return USAGE; } return OK; } Status doMain(Args &&args) override { return mCommand->main(std::move(args)); } protected: std::unique_ptr mCommand; }; } // namespace idlcli } // namespace android #endif // FRAMEWORK_NATIVE_CMDS_IDLCLI_UTILS_H_