| /* |
| * 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 <android/binder_enums.h> |
| #include <hidl/HidlSupport.h> |
| |
| #include <iomanip> |
| #include <iostream> |
| #include <map> |
| #include <sstream> |
| #include <string> |
| #include <vector> |
| |
| namespace android { |
| namespace idlcli { |
| |
| namespace overrides { |
| |
| namespace details { |
| |
| template <typename T> |
| inline std::istream &operator>>(std::istream &stream, T &out) { |
| auto pos = stream.tellg(); |
| auto tmp = +out; |
| auto min = +std::numeric_limits<T>::min(); |
| auto max = +std::numeric_limits<T>::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 <typename T, typename R = ndk::enum_range<T>> |
| inline std::istream &operator>>(std::istream &stream, T &out) { |
| using overrides::operator>>; |
| auto validRange = R(); |
| auto pos = stream.tellg(); |
| std::underlying_type_t<T> in; |
| T tmp; |
| stream >> in; |
| if (!stream) { |
| return stream; |
| } |
| tmp = static_cast<T>(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 <typename T = std::string> |
| std::optional<T> get() { |
| return get<T>(false); |
| } |
| |
| template <typename T = std::string> |
| std::optional<T> pop() { |
| return get<T>(true); |
| } |
| |
| bool empty() { return mArgs.empty(); } |
| |
| private: |
| template <typename T> |
| std::optional<T> 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<std::string_view> mArgs; |
| }; |
| |
| class Command { |
| protected: |
| struct Usage { |
| std::string name; |
| std::vector<std::string> details; |
| }; |
| using UsageDetails = std::vector<Usage>; |
| |
| 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 <typename T> |
| class CommandRegistry { |
| private: |
| using CommandCreator = std::function<std::unique_ptr<Command>()>; |
| |
| public: |
| template <typename U> |
| static CommandCreator Register(const std::string name) { |
| Instance()->mCommands[name] = [] { return std::make_unique<U>(); }; |
| return Instance()->mCommands[name]; |
| } |
| |
| static std::unique_ptr<Command> 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<std::string> 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<const std::string, CommandCreator> mCommands; |
| }; |
| |
| template <typename T> |
| class CommandWithSubcommands : public Command { |
| protected: |
| Status doArgs(Args &args) override { |
| mCommand = CommandRegistry<T>::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<Command> mCommand; |
| }; |
| |
| } // namespace idlcli |
| } // namespace android |
| |
| #endif // FRAMEWORK_NATIVE_CMDS_IDLCLI_UTILS_H_ |