blob: a8e595470df8f8356abee370f97bfa843e53c2cd [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
20#include <hidl/HidlSupport.h>
21
22#include <iomanip>
23#include <iostream>
24#include <map>
25#include <sstream>
26#include <string>
27#include <vector>
28
29namespace android {
30namespace idlcli {
31
32namespace overrides {
33
34namespace details {
35
36template <typename T>
37inline std::istream &operator>>(std::istream &stream, T &out) {
38 auto pos = stream.tellg();
39 auto tmp = +out;
40 auto min = +std::numeric_limits<T>::min();
41 auto max = +std::numeric_limits<T>::max();
42 stream >> tmp;
43 if (!stream) {
44 return stream;
45 }
46 if (tmp < min || tmp > max) {
47 stream.seekg(pos);
48 stream.setstate(std::ios_base::failbit);
49 return stream;
50 }
51 out = tmp;
52 return stream;
53}
54
55} // namespace details
56
57// override for default behavior of treating as a character
58inline std::istream &operator>>(std::istream &stream, int8_t &out) {
59 return details::operator>>(stream, out);
60}
61
62// override for default behavior of treating as a character
63inline std::istream &operator>>(std::istream &stream, uint8_t &out) {
64 return details::operator>>(stream, out);
65}
66
67} // namespace overrides
68
69template <typename T, typename R = hardware::hidl_enum_range<T>>
70inline std::istream &operator>>(std::istream &stream, T &out) {
71 using overrides::operator>>;
72 auto validRange = R();
73 auto pos = stream.tellg();
74 std::underlying_type_t<T> in;
75 T tmp;
76 stream >> in;
77 if (!stream) {
78 return stream;
79 }
80 tmp = static_cast<T>(in);
81 if (tmp < *validRange.begin() || tmp > *std::prev(validRange.end())) {
82 stream.seekg(pos);
83 stream.setstate(std::ios_base::failbit);
84 return stream;
85 }
86 out = tmp;
87 return stream;
88}
89
90enum Status : unsigned int {
91 OK,
92 USAGE,
93 UNAVAILABLE,
94 ERROR,
95};
96
97class Args {
98public:
99 Args(const int argc, const char *const argv[]) {
100 for (int argi = 0; argi < argc; argi++) {
101 mArgs.emplace_back(std::string_view(argv[argi]));
102 }
103 }
104
105 template <typename T = std::string>
106 std::optional<T> get() {
107 return get<T>(false);
108 }
109
110 template <typename T = std::string>
111 std::optional<T> pop() {
112 return get<T>(true);
113 }
114
115 bool empty() { return mArgs.empty(); }
116
117private:
118 template <typename T>
119 std::optional<T> get(bool erase) {
120 using idlcli::operator>>;
121 using overrides::operator>>;
122 T retValue;
123
124 if (mArgs.empty()) {
125 return {};
126 }
127
128 std::stringstream stream{std::string{mArgs.front()}};
129 stream >> std::setbase(0) >> retValue;
130 if (!stream || !stream.eof()) {
131 return {};
132 }
133
134 if (erase) {
135 mArgs.erase(mArgs.begin());
136 }
137
138 return retValue;
139 }
140
141 std::vector<std::string_view> mArgs;
142};
143
144class Command {
145protected:
146 struct Usage {
147 std::string name;
148 std::vector<std::string> details;
149 };
150 using UsageDetails = std::vector<Usage>;
151
152public:
153 virtual ~Command() = default;
154
155 Status main(Args &&args) {
156 Status status = doArgsAndMain(std::move(args));
157 if (status == USAGE) {
158 printUsage();
159 return ERROR;
160 }
161 if (status == UNAVAILABLE) {
162 std::cerr << "The requested operation is unavailable." << std::endl;
163 return ERROR;
164 }
165 return status;
166 }
167
168private:
169 virtual std::string getDescription() const = 0;
170 virtual std::string getUsageSummary() const = 0;
171 virtual UsageDetails getUsageDetails() const = 0;
172 virtual Status doArgs(Args &args) = 0;
173 virtual Status doMain(Args &&args) = 0;
174
175 void printUsage() const {
176 std::cerr << "Description:\n " << getDescription() << std::endl;
177 std::cerr << "Usage:\n " << mName << " " << getUsageSummary() << std::endl;
178
179 std::cerr << "Details:" << std::endl;
180 size_t entryNameWidth = 0;
181 for (auto &entry : getUsageDetails()) {
182 entryNameWidth = std::max(entryNameWidth, entry.name.length());
183 }
184 for (auto &entry : getUsageDetails()) {
185 auto prefix = entry.name;
186 for (auto &line : entry.details) {
187 std::cerr << " " << std::left << std::setw(entryNameWidth + 8) << prefix << line
188 << std::endl;
189 prefix = "";
190 }
191 }
192 }
193
194 Status doArgsAndMain(Args &&args) {
195 Status status;
196 mName = *args.pop();
197 if ((status = doArgs(args)) != OK) {
198 return status;
199 }
200 if ((status = doMain(std::move(args))) != OK) {
201 return status;
202 }
203 return OK;
204 }
205
206protected:
207 std::string mName;
208};
209
210template <typename T>
211class CommandRegistry {
212private:
213 using CommandCreator = std::function<std::unique_ptr<Command>()>;
214
215public:
216 template <typename U>
217 static CommandCreator Register(const std::string name) {
218 Instance()->mCommands[name] = [] { return std::make_unique<U>(); };
219 return Instance()->mCommands[name];
220 }
221
222 static std::unique_ptr<Command> Create(const std::string name) {
223 auto it = Instance()->mCommands.find(name);
224 if (it == Instance()->mCommands.end()) {
225 return nullptr;
226 }
227 return it->second();
228 }
229
230 static auto List() {
231 std::vector<std::string> list;
232 for (auto &it : Instance()->mCommands) {
233 list.push_back(it.first);
234 }
235 std::sort(list.begin(), list.end());
236 return list;
237 }
238
239private:
240 static CommandRegistry *Instance() {
241 static CommandRegistry sRegistry;
242 return &sRegistry;
243 }
244
245private:
246 std::map<const std::string, CommandCreator> mCommands;
247};
248
249template <typename T>
250class CommandWithSubcommands : public Command {
251private:
252 Status doArgs(Args &args) override {
253 mCommand = CommandRegistry<T>::Create(*args.get());
254 if (!mCommand) {
255 std::cerr << "Invalid Command!" << std::endl;
256 return USAGE;
257 }
258 return OK;
259 }
260
261 Status doMain(Args &&args) override { return mCommand->main(std::move(args)); }
262
263protected:
264 std::unique_ptr<Command> mCommand;
265};
266
267} // namespace idlcli
268} // namespace android
269
270#endif // FRAMEWORK_NATIVE_CMDS_IDLCLI_UTILS_H_