blob: 888b3a5b129d3569298e64877330460f85a20977 [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>
Ryan Prichardd9a3ffa2022-08-30 14:24:57 -070020#include <cassert>
Mårten Kongstad02751232018-04-27 13:16:32 +020021#include <iomanip>
Mårten Kongstad02751232018-04-27 13:16:32 +020022#include <memory>
Yurii Zubrytskyia29f3c02023-05-12 16:05:13 -070023#include <ostream>
Mårten Kongstad02751232018-04-27 13:16:32 +020024#include <set>
Mårten Kongstad0c6ff1d2019-02-07 02:21:56 +010025#include <sstream>
Mårten Kongstad02751232018-04-27 13:16:32 +020026#include <string>
27#include <vector>
28
29#include "android-base/macros.h"
Mårten Kongstad0c6ff1d2019-02-07 02:21:56 +010030#include "idmap2/Result.h"
Mårten Kongstad02751232018-04-27 13:16:32 +020031
Mårten Kongstad0eba72a2018-11-29 08:23:14 +010032namespace android::idmap2 {
Mårten Kongstad02751232018-04-27 13:16:32 +020033
34std::unique_ptr<std::vector<std::string>> CommandLineOptions::ConvertArgvToVector(
35 int argc, const char** argv) {
Mårten Kongstad0eba72a2018-11-29 08:23:14 +010036 return std::make_unique<std::vector<std::string>>(argv + 1, argv + argc);
Mårten Kongstad02751232018-04-27 13:16:32 +020037}
38
39CommandLineOptions& CommandLineOptions::OptionalFlag(const std::string& name,
40 const std::string& description, bool* value) {
41 assert(value != nullptr);
42 auto func = [value](const std::string& arg ATTRIBUTE_UNUSED) -> void { *value = true; };
43 options_.push_back(Option{name, description, func, Option::COUNT_OPTIONAL, false});
44 return *this;
45}
46
47CommandLineOptions& CommandLineOptions::MandatoryOption(const std::string& name,
48 const std::string& description,
49 std::string* value) {
50 assert(value != nullptr);
51 auto func = [value](const std::string& arg) -> void { *value = arg; };
52 options_.push_back(Option{name, description, func, Option::COUNT_EXACTLY_ONCE, true});
53 return *this;
54}
55
56CommandLineOptions& CommandLineOptions::MandatoryOption(const std::string& name,
57 const std::string& description,
58 std::vector<std::string>* value) {
59 assert(value != nullptr);
60 auto func = [value](const std::string& arg) -> void { value->push_back(arg); };
61 options_.push_back(Option{name, description, func, Option::COUNT_ONCE_OR_MORE, true});
62 return *this;
63}
64
65CommandLineOptions& CommandLineOptions::OptionalOption(const std::string& name,
66 const std::string& description,
67 std::string* value) {
68 assert(value != nullptr);
69 auto func = [value](const std::string& arg) -> void { *value = arg; };
70 options_.push_back(Option{name, description, func, Option::COUNT_OPTIONAL, true});
71 return *this;
72}
73
Mårten Kongstadd10d06d2019-01-07 17:26:25 -080074CommandLineOptions& CommandLineOptions::OptionalOption(const std::string& name,
75 const std::string& description,
76 std::vector<std::string>* value) {
77 assert(value != nullptr);
78 auto func = [value](const std::string& arg) -> void { value->push_back(arg); };
79 options_.push_back(Option{name, description, func, Option::COUNT_OPTIONAL_ONCE_OR_MORE, true});
80 return *this;
81}
82
Mårten Kongstad0c6ff1d2019-02-07 02:21:56 +010083Result<Unit> CommandLineOptions::Parse(const std::vector<std::string>& argv) const {
Mårten Kongstad02751232018-04-27 13:16:32 +020084 const auto pivot = std::partition(options_.begin(), options_.end(), [](const Option& opt) {
Mårten Kongstadd10d06d2019-01-07 17:26:25 -080085 return opt.count != Option::COUNT_OPTIONAL && opt.count != Option::COUNT_OPTIONAL_ONCE_OR_MORE;
Mårten Kongstad02751232018-04-27 13:16:32 +020086 });
87 std::set<std::string> mandatory_opts;
88 std::transform(options_.begin(), pivot, std::inserter(mandatory_opts, mandatory_opts.end()),
89 [](const Option& opt) -> std::string { return opt.name; });
90
91 const size_t argv_size = argv.size();
92 for (size_t i = 0; i < argv_size; i++) {
93 const std::string arg = argv[i];
94 if ("--help" == arg || "-h" == arg) {
Mårten Kongstad0c6ff1d2019-02-07 02:21:56 +010095 std::stringstream stream;
96 Usage(stream);
97 return Error("%s", stream.str().c_str());
Mårten Kongstad02751232018-04-27 13:16:32 +020098 }
99 bool match = false;
100 for (const Option& opt : options_) {
101 if (opt.name == arg) {
102 match = true;
103
104 if (opt.argument) {
105 i++;
106 if (i >= argv_size) {
Mårten Kongstad0c6ff1d2019-02-07 02:21:56 +0100107 std::stringstream stream;
108 Usage(stream);
109 return Error("%s: missing argument\n%s", opt.name.c_str(), stream.str().c_str());
Mårten Kongstad02751232018-04-27 13:16:32 +0200110 }
111 }
112 opt.action(argv[i]);
113 mandatory_opts.erase(opt.name);
114 break;
115 }
116 }
117 if (!match) {
Mårten Kongstad0c6ff1d2019-02-07 02:21:56 +0100118 std::stringstream stream;
119 Usage(stream);
120 return Error("%s: unknown option\n%s", arg.c_str(), stream.str().c_str());
Mårten Kongstad02751232018-04-27 13:16:32 +0200121 }
122 }
123
124 if (!mandatory_opts.empty()) {
Mårten Kongstad0c6ff1d2019-02-07 02:21:56 +0100125 std::stringstream stream;
126 bool separator = false;
Mårten Kongstad0eba72a2018-11-29 08:23:14 +0100127 for (const auto& opt : mandatory_opts) {
Mårten Kongstad0c6ff1d2019-02-07 02:21:56 +0100128 if (separator) {
129 stream << ", ";
130 }
131 separator = true;
132 stream << opt << ": missing mandatory option";
Mårten Kongstad02751232018-04-27 13:16:32 +0200133 }
Yurii Zubrytskyia29f3c02023-05-12 16:05:13 -0700134 stream << '\n';
Mårten Kongstad0c6ff1d2019-02-07 02:21:56 +0100135 Usage(stream);
136 return Error("%s", stream.str().c_str());
Mårten Kongstad02751232018-04-27 13:16:32 +0200137 }
Mårten Kongstad0c6ff1d2019-02-07 02:21:56 +0100138 return Unit{};
Mårten Kongstad02751232018-04-27 13:16:32 +0200139}
140
141void CommandLineOptions::Usage(std::ostream& out) const {
142 size_t maxLength = 0;
143 out << "usage: " << name_;
144 for (const Option& opt : options_) {
Mårten Kongstadd10d06d2019-01-07 17:26:25 -0800145 const bool mandatory =
146 opt.count != Option::COUNT_OPTIONAL && opt.count != Option::COUNT_OPTIONAL_ONCE_OR_MORE;
Mårten Kongstad02751232018-04-27 13:16:32 +0200147 out << " ";
148 if (!mandatory) {
149 out << "[";
150 }
151 if (opt.argument) {
152 out << opt.name << " arg";
153 maxLength = std::max(maxLength, opt.name.size() + 4);
154 } else {
155 out << opt.name;
156 maxLength = std::max(maxLength, opt.name.size());
157 }
Mårten Kongstadd10d06d2019-01-07 17:26:25 -0800158
159 if (opt.count == Option::COUNT_OPTIONAL_ONCE_OR_MORE) {
160 out << " [..]";
161 }
162
Mårten Kongstad02751232018-04-27 13:16:32 +0200163 if (!mandatory) {
164 out << "]";
165 }
Mårten Kongstadd10d06d2019-01-07 17:26:25 -0800166
Mårten Kongstad02751232018-04-27 13:16:32 +0200167 if (opt.count == Option::COUNT_ONCE_OR_MORE) {
168 out << " [" << opt.name << " arg [..]]";
169 }
170 }
Yurii Zubrytskyia29f3c02023-05-12 16:05:13 -0700171 out << "\n\n";
Mårten Kongstad02751232018-04-27 13:16:32 +0200172 for (const Option& opt : options_) {
173 out << std::left << std::setw(maxLength);
174 if (opt.argument) {
175 out << (opt.name + " arg");
176 } else {
177 out << opt.name;
178 }
179 out << " " << opt.description;
Mårten Kongstadd10d06d2019-01-07 17:26:25 -0800180 if (opt.count == Option::COUNT_ONCE_OR_MORE ||
181 opt.count == Option::COUNT_OPTIONAL_ONCE_OR_MORE) {
Mårten Kongstad02751232018-04-27 13:16:32 +0200182 out << " (can be provided multiple times)";
183 }
Yurii Zubrytskyia29f3c02023-05-12 16:05:13 -0700184 out << '\n';
Mårten Kongstad02751232018-04-27 13:16:32 +0200185 }
186}
187
Mårten Kongstad0eba72a2018-11-29 08:23:14 +0100188} // namespace android::idmap2