blob: 262f2e50b69b025d5cbc227e282d4f5911a3781e [file] [log] [blame]
Harpreet "Eli" Sanghae47ae682019-06-05 16:52:47 +09001/*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef FRAMEWORK_NATIVE_CMDS_IDLCLI_UTILS_H_
18#define FRAMEWORK_NATIVE_CMDS_IDLCLI_UTILS_H_
19
Harpreet \"Eli\" Sanghaeb8505c2020-04-10 12:40:23 +090020#include <android/binder_enums.h>
Harpreet "Eli" Sanghae47ae682019-06-05 16:52:47 +090021#include <hidl/HidlSupport.h>
22
23#include <iomanip>
24#include <iostream>
25#include <map>
26#include <sstream>
27#include <string>
28#include <vector>
29
30namespace android {
31namespace idlcli {
32
33namespace overrides {
34
35namespace details {
36
37template <typename T>
38inline std::istream &operator>>(std::istream &stream, T &out) {
39 auto pos = stream.tellg();
40 auto tmp = +out;
41 auto min = +std::numeric_limits<T>::min();
42 auto max = +std::numeric_limits<T>::max();
43 stream >> tmp;
44 if (!stream) {
45 return stream;
46 }
47 if (tmp < min || tmp > max) {
48 stream.seekg(pos);
49 stream.setstate(std::ios_base::failbit);
50 return stream;
51 }
52 out = tmp;
53 return stream;
54}
55
56} // namespace details
57
58// override for default behavior of treating as a character
59inline std::istream &operator>>(std::istream &stream, int8_t &out) {
60 return details::operator>>(stream, out);
61}
62
63// override for default behavior of treating as a character
64inline std::istream &operator>>(std::istream &stream, uint8_t &out) {
65 return details::operator>>(stream, out);
66}
67
68} // namespace overrides
69
Harpreet \"Eli\" Sanghaeb8505c2020-04-10 12:40:23 +090070template <typename T, typename R = ndk::enum_range<T>>
Harpreet "Eli" Sanghae47ae682019-06-05 16:52:47 +090071inline std::istream &operator>>(std::istream &stream, T &out) {
72 using overrides::operator>>;
73 auto validRange = R();
74 auto pos = stream.tellg();
75 std::underlying_type_t<T> in;
76 T tmp;
77 stream >> in;
78 if (!stream) {
79 return stream;
80 }
81 tmp = static_cast<T>(in);
82 if (tmp < *validRange.begin() || tmp > *std::prev(validRange.end())) {
83 stream.seekg(pos);
84 stream.setstate(std::ios_base::failbit);
85 return stream;
86 }
87 out = tmp;
88 return stream;
89}
90
91enum Status : unsigned int {
92 OK,
93 USAGE,
94 UNAVAILABLE,
95 ERROR,
96};
97
98class Args {
99public:
100 Args(const int argc, const char *const argv[]) {
101 for (int argi = 0; argi < argc; argi++) {
102 mArgs.emplace_back(std::string_view(argv[argi]));
103 }
104 }
105
106 template <typename T = std::string>
107 std::optional<T> get() {
108 return get<T>(false);
109 }
110
111 template <typename T = std::string>
112 std::optional<T> pop() {
113 return get<T>(true);
114 }
115
116 bool empty() { return mArgs.empty(); }
117
118private:
119 template <typename T>
120 std::optional<T> get(bool erase) {
121 using idlcli::operator>>;
122 using overrides::operator>>;
123 T retValue;
124
125 if (mArgs.empty()) {
126 return {};
127 }
128
129 std::stringstream stream{std::string{mArgs.front()}};
130 stream >> std::setbase(0) >> retValue;
131 if (!stream || !stream.eof()) {
132 return {};
133 }
134
135 if (erase) {
136 mArgs.erase(mArgs.begin());
137 }
138
139 return retValue;
140 }
141
142 std::vector<std::string_view> mArgs;
143};
144
145class Command {
146protected:
147 struct Usage {
148 std::string name;
149 std::vector<std::string> details;
150 };
151 using UsageDetails = std::vector<Usage>;
152
153public:
154 virtual ~Command() = default;
155
156 Status main(Args &&args) {
157 Status status = doArgsAndMain(std::move(args));
158 if (status == USAGE) {
159 printUsage();
160 return ERROR;
161 }
162 if (status == UNAVAILABLE) {
163 std::cerr << "The requested operation is unavailable." << std::endl;
164 return ERROR;
165 }
166 return status;
167 }
168
169private:
170 virtual std::string getDescription() const = 0;
171 virtual std::string getUsageSummary() const = 0;
172 virtual UsageDetails getUsageDetails() const = 0;
173 virtual Status doArgs(Args &args) = 0;
174 virtual Status doMain(Args &&args) = 0;
175
176 void printUsage() const {
177 std::cerr << "Description:\n " << getDescription() << std::endl;
178 std::cerr << "Usage:\n " << mName << " " << getUsageSummary() << std::endl;
179
180 std::cerr << "Details:" << std::endl;
181 size_t entryNameWidth = 0;
182 for (auto &entry : getUsageDetails()) {
183 entryNameWidth = std::max(entryNameWidth, entry.name.length());
184 }
185 for (auto &entry : getUsageDetails()) {
186 auto prefix = entry.name;
187 for (auto &line : entry.details) {
188 std::cerr << " " << std::left << std::setw(entryNameWidth + 8) << prefix << line
189 << std::endl;
190 prefix = "";
191 }
192 }
193 }
194
195 Status doArgsAndMain(Args &&args) {
196 Status status;
197 mName = *args.pop();
198 if ((status = doArgs(args)) != OK) {
199 return status;
200 }
201 if ((status = doMain(std::move(args))) != OK) {
202 return status;
203 }
204 return OK;
205 }
206
207protected:
208 std::string mName;
209};
210
211template <typename T>
212class CommandRegistry {
213private:
214 using CommandCreator = std::function<std::unique_ptr<Command>()>;
215
216public:
217 template <typename U>
218 static CommandCreator Register(const std::string name) {
219 Instance()->mCommands[name] = [] { return std::make_unique<U>(); };
220 return Instance()->mCommands[name];
221 }
222
223 static std::unique_ptr<Command> Create(const std::string name) {
224 auto it = Instance()->mCommands.find(name);
225 if (it == Instance()->mCommands.end()) {
226 return nullptr;
227 }
228 return it->second();
229 }
230
231 static auto List() {
232 std::vector<std::string> list;
233 for (auto &it : Instance()->mCommands) {
234 list.push_back(it.first);
235 }
236 std::sort(list.begin(), list.end());
237 return list;
238 }
239
240private:
241 static CommandRegistry *Instance() {
242 static CommandRegistry sRegistry;
243 return &sRegistry;
244 }
245
246private:
247 std::map<const std::string, CommandCreator> mCommands;
248};
249
250template <typename T>
251class CommandWithSubcommands : public Command {
Harpreet Eli Sangha32226c92020-08-07 16:28:51 +0900252protected:
Harpreet "Eli" Sanghae47ae682019-06-05 16:52:47 +0900253 Status doArgs(Args &args) override {
254 mCommand = CommandRegistry<T>::Create(*args.get());
255 if (!mCommand) {
256 std::cerr << "Invalid Command!" << std::endl;
257 return USAGE;
258 }
259 return OK;
260 }
261
262 Status doMain(Args &&args) override { return mCommand->main(std::move(args)); }
263
264protected:
265 std::unique_ptr<Command> mCommand;
266};
267
268} // namespace idlcli
269} // namespace android
270
271#endif // FRAMEWORK_NATIVE_CMDS_IDLCLI_UTILS_H_