blob: 5b0ae92df887839d924a70681b4cc2ddefd5e8e4 [file] [log] [blame]
Mårten Kongstad02751232018-04-27 13:16:32 +02001/*
2 * Copyright (C) 2018 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
Ryan Mitchell52e1f7a2019-04-12 12:31:42 -070017#include "idmap2/CommandLineOptions.h"
18
Mårten Kongstad02751232018-04-27 13:16:32 +020019#include <algorithm>
20#include <iomanip>
21#include <iostream>
22#include <memory>
23#include <set>
Mårten Kongstad0c6ff1d2019-02-07 02:21:56 +010024#include <sstream>
Mårten Kongstad02751232018-04-27 13:16:32 +020025#include <string>
26#include <vector>
27
28#include "android-base/macros.h"
Mårten Kongstad0c6ff1d2019-02-07 02:21:56 +010029#include "idmap2/Result.h"
Mårten Kongstad02751232018-04-27 13:16:32 +020030
Mårten Kongstad0eba72a2018-11-29 08:23:14 +010031namespace android::idmap2 {
Mårten Kongstad02751232018-04-27 13:16:32 +020032
33std::unique_ptr<std::vector<std::string>> CommandLineOptions::ConvertArgvToVector(
34 int argc, const char** argv) {
Mårten Kongstad0eba72a2018-11-29 08:23:14 +010035 return std::make_unique<std::vector<std::string>>(argv + 1, argv + argc);
Mårten Kongstad02751232018-04-27 13:16:32 +020036}
37
38CommandLineOptions& CommandLineOptions::OptionalFlag(const std::string& name,
39 const std::string& description, bool* value) {
40 assert(value != nullptr);
41 auto func = [value](const std::string& arg ATTRIBUTE_UNUSED) -> void { *value = true; };
42 options_.push_back(Option{name, description, func, Option::COUNT_OPTIONAL, false});
43 return *this;
44}
45
46CommandLineOptions& CommandLineOptions::MandatoryOption(const std::string& name,
47 const std::string& description,
48 std::string* value) {
49 assert(value != nullptr);
50 auto func = [value](const std::string& arg) -> void { *value = arg; };
51 options_.push_back(Option{name, description, func, Option::COUNT_EXACTLY_ONCE, true});
52 return *this;
53}
54
55CommandLineOptions& CommandLineOptions::MandatoryOption(const std::string& name,
56 const std::string& description,
57 std::vector<std::string>* value) {
58 assert(value != nullptr);
59 auto func = [value](const std::string& arg) -> void { value->push_back(arg); };
60 options_.push_back(Option{name, description, func, Option::COUNT_ONCE_OR_MORE, true});
61 return *this;
62}
63
64CommandLineOptions& CommandLineOptions::OptionalOption(const std::string& name,
65 const std::string& description,
66 std::string* value) {
67 assert(value != nullptr);
68 auto func = [value](const std::string& arg) -> void { *value = arg; };
69 options_.push_back(Option{name, description, func, Option::COUNT_OPTIONAL, true});
70 return *this;
71}
72
Mårten Kongstadd10d06d2019-01-07 17:26:25 -080073CommandLineOptions& CommandLineOptions::OptionalOption(const std::string& name,
74 const std::string& description,
75 std::vector<std::string>* value) {
76 assert(value != nullptr);
77 auto func = [value](const std::string& arg) -> void { value->push_back(arg); };
78 options_.push_back(Option{name, description, func, Option::COUNT_OPTIONAL_ONCE_OR_MORE, true});
79 return *this;
80}
81
Mårten Kongstad0c6ff1d2019-02-07 02:21:56 +010082Result<Unit> CommandLineOptions::Parse(const std::vector<std::string>& argv) const {
Mårten Kongstad02751232018-04-27 13:16:32 +020083 const auto pivot = std::partition(options_.begin(), options_.end(), [](const Option& opt) {
Mårten Kongstadd10d06d2019-01-07 17:26:25 -080084 return opt.count != Option::COUNT_OPTIONAL && opt.count != Option::COUNT_OPTIONAL_ONCE_OR_MORE;
Mårten Kongstad02751232018-04-27 13:16:32 +020085 });
86 std::set<std::string> mandatory_opts;
87 std::transform(options_.begin(), pivot, std::inserter(mandatory_opts, mandatory_opts.end()),
88 [](const Option& opt) -> std::string { return opt.name; });
89
90 const size_t argv_size = argv.size();
91 for (size_t i = 0; i < argv_size; i++) {
92 const std::string arg = argv[i];
93 if ("--help" == arg || "-h" == arg) {
Mårten Kongstad0c6ff1d2019-02-07 02:21:56 +010094 std::stringstream stream;
95 Usage(stream);
96 return Error("%s", stream.str().c_str());
Mårten Kongstad02751232018-04-27 13:16:32 +020097 }
98 bool match = false;
99 for (const Option& opt : options_) {
100 if (opt.name == arg) {
101 match = true;
102
103 if (opt.argument) {
104 i++;
105 if (i >= argv_size) {
Mårten Kongstad0c6ff1d2019-02-07 02:21:56 +0100106 std::stringstream stream;
107 Usage(stream);
108 return Error("%s: missing argument\n%s", opt.name.c_str(), stream.str().c_str());
Mårten Kongstad02751232018-04-27 13:16:32 +0200109 }
110 }
111 opt.action(argv[i]);
112 mandatory_opts.erase(opt.name);
113 break;
114 }
115 }
116 if (!match) {
Mårten Kongstad0c6ff1d2019-02-07 02:21:56 +0100117 std::stringstream stream;
118 Usage(stream);
119 return Error("%s: unknown option\n%s", arg.c_str(), stream.str().c_str());
Mårten Kongstad02751232018-04-27 13:16:32 +0200120 }
121 }
122
123 if (!mandatory_opts.empty()) {
Mårten Kongstad0c6ff1d2019-02-07 02:21:56 +0100124 std::stringstream stream;
125 bool separator = false;
Mårten Kongstad0eba72a2018-11-29 08:23:14 +0100126 for (const auto& opt : mandatory_opts) {
Mårten Kongstad0c6ff1d2019-02-07 02:21:56 +0100127 if (separator) {
128 stream << ", ";
129 }
130 separator = true;
131 stream << opt << ": missing mandatory option";
Mårten Kongstad02751232018-04-27 13:16:32 +0200132 }
Mårten Kongstad0c6ff1d2019-02-07 02:21:56 +0100133 stream << std::endl;
134 Usage(stream);
135 return Error("%s", stream.str().c_str());
Mårten Kongstad02751232018-04-27 13:16:32 +0200136 }
Mårten Kongstad0c6ff1d2019-02-07 02:21:56 +0100137 return Unit{};
Mårten Kongstad02751232018-04-27 13:16:32 +0200138}
139
140void CommandLineOptions::Usage(std::ostream& out) const {
141 size_t maxLength = 0;
142 out << "usage: " << name_;
143 for (const Option& opt : options_) {
Mårten Kongstadd10d06d2019-01-07 17:26:25 -0800144 const bool mandatory =
145 opt.count != Option::COUNT_OPTIONAL && opt.count != Option::COUNT_OPTIONAL_ONCE_OR_MORE;
Mårten Kongstad02751232018-04-27 13:16:32 +0200146 out << " ";
147 if (!mandatory) {
148 out << "[";
149 }
150 if (opt.argument) {
151 out << opt.name << " arg";
152 maxLength = std::max(maxLength, opt.name.size() + 4);
153 } else {
154 out << opt.name;
155 maxLength = std::max(maxLength, opt.name.size());
156 }
Mårten Kongstadd10d06d2019-01-07 17:26:25 -0800157
158 if (opt.count == Option::COUNT_OPTIONAL_ONCE_OR_MORE) {
159 out << " [..]";
160 }
161
Mårten Kongstad02751232018-04-27 13:16:32 +0200162 if (!mandatory) {
163 out << "]";
164 }
Mårten Kongstadd10d06d2019-01-07 17:26:25 -0800165
Mårten Kongstad02751232018-04-27 13:16:32 +0200166 if (opt.count == Option::COUNT_ONCE_OR_MORE) {
167 out << " [" << opt.name << " arg [..]]";
168 }
169 }
170 out << std::endl << std::endl;
171 for (const Option& opt : options_) {
172 out << std::left << std::setw(maxLength);
173 if (opt.argument) {
174 out << (opt.name + " arg");
175 } else {
176 out << opt.name;
177 }
178 out << " " << opt.description;
Mårten Kongstadd10d06d2019-01-07 17:26:25 -0800179 if (opt.count == Option::COUNT_ONCE_OR_MORE ||
180 opt.count == Option::COUNT_OPTIONAL_ONCE_OR_MORE) {
Mårten Kongstad02751232018-04-27 13:16:32 +0200181 out << " (can be provided multiple times)";
182 }
183 out << std::endl;
184 }
185}
186
Mårten Kongstad0eba72a2018-11-29 08:23:14 +0100187} // namespace android::idmap2