blob: 2da05b36b7750d492d2895a14ebc4137a161b657 [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>
Ryan Prichard30170b12023-08-31 00:14:25 -070022#include <iterator>
Mårten Kongstad02751232018-04-27 13:16:32 +020023#include <memory>
Yurii Zubrytskyia29f3c02023-05-12 16:05:13 -070024#include <ostream>
Mårten Kongstad02751232018-04-27 13:16:32 +020025#include <set>
Mårten Kongstad0c6ff1d2019-02-07 02:21:56 +010026#include <sstream>
Mårten Kongstad02751232018-04-27 13:16:32 +020027#include <string>
28#include <vector>
29
30#include "android-base/macros.h"
Mårten Kongstad0c6ff1d2019-02-07 02:21:56 +010031#include "idmap2/Result.h"
Mårten Kongstad02751232018-04-27 13:16:32 +020032
Mårten Kongstad0eba72a2018-11-29 08:23:14 +010033namespace android::idmap2 {
Mårten Kongstad02751232018-04-27 13:16:32 +020034
35std::unique_ptr<std::vector<std::string>> CommandLineOptions::ConvertArgvToVector(
36 int argc, const char** argv) {
Mårten Kongstad0eba72a2018-11-29 08:23:14 +010037 return std::make_unique<std::vector<std::string>>(argv + 1, argv + argc);
Mårten Kongstad02751232018-04-27 13:16:32 +020038}
39
40CommandLineOptions& CommandLineOptions::OptionalFlag(const std::string& name,
41 const std::string& description, bool* value) {
42 assert(value != nullptr);
43 auto func = [value](const std::string& arg ATTRIBUTE_UNUSED) -> void { *value = true; };
44 options_.push_back(Option{name, description, func, Option::COUNT_OPTIONAL, false});
45 return *this;
46}
47
48CommandLineOptions& CommandLineOptions::MandatoryOption(const std::string& name,
49 const std::string& description,
50 std::string* value) {
51 assert(value != nullptr);
52 auto func = [value](const std::string& arg) -> void { *value = arg; };
53 options_.push_back(Option{name, description, func, Option::COUNT_EXACTLY_ONCE, true});
54 return *this;
55}
56
57CommandLineOptions& CommandLineOptions::MandatoryOption(const std::string& name,
58 const std::string& description,
59 std::vector<std::string>* value) {
60 assert(value != nullptr);
61 auto func = [value](const std::string& arg) -> void { value->push_back(arg); };
62 options_.push_back(Option{name, description, func, Option::COUNT_ONCE_OR_MORE, true});
63 return *this;
64}
65
66CommandLineOptions& CommandLineOptions::OptionalOption(const std::string& name,
67 const std::string& description,
68 std::string* value) {
69 assert(value != nullptr);
70 auto func = [value](const std::string& arg) -> void { *value = arg; };
71 options_.push_back(Option{name, description, func, Option::COUNT_OPTIONAL, true});
72 return *this;
73}
74
Mårten Kongstadd10d06d2019-01-07 17:26:25 -080075CommandLineOptions& CommandLineOptions::OptionalOption(const std::string& name,
76 const std::string& description,
77 std::vector<std::string>* value) {
78 assert(value != nullptr);
79 auto func = [value](const std::string& arg) -> void { value->push_back(arg); };
80 options_.push_back(Option{name, description, func, Option::COUNT_OPTIONAL_ONCE_OR_MORE, true});
81 return *this;
82}
83
Mårten Kongstad0c6ff1d2019-02-07 02:21:56 +010084Result<Unit> CommandLineOptions::Parse(const std::vector<std::string>& argv) const {
Mårten Kongstad02751232018-04-27 13:16:32 +020085 const auto pivot = std::partition(options_.begin(), options_.end(), [](const Option& opt) {
Mårten Kongstadd10d06d2019-01-07 17:26:25 -080086 return opt.count != Option::COUNT_OPTIONAL && opt.count != Option::COUNT_OPTIONAL_ONCE_OR_MORE;
Mårten Kongstad02751232018-04-27 13:16:32 +020087 });
88 std::set<std::string> mandatory_opts;
89 std::transform(options_.begin(), pivot, std::inserter(mandatory_opts, mandatory_opts.end()),
90 [](const Option& opt) -> std::string { return opt.name; });
91
92 const size_t argv_size = argv.size();
93 for (size_t i = 0; i < argv_size; i++) {
94 const std::string arg = argv[i];
95 if ("--help" == arg || "-h" == arg) {
Mårten Kongstad0c6ff1d2019-02-07 02:21:56 +010096 std::stringstream stream;
97 Usage(stream);
98 return Error("%s", stream.str().c_str());
Mårten Kongstad02751232018-04-27 13:16:32 +020099 }
100 bool match = false;
101 for (const Option& opt : options_) {
102 if (opt.name == arg) {
103 match = true;
104
105 if (opt.argument) {
106 i++;
107 if (i >= argv_size) {
Mårten Kongstad0c6ff1d2019-02-07 02:21:56 +0100108 std::stringstream stream;
109 Usage(stream);
110 return Error("%s: missing argument\n%s", opt.name.c_str(), stream.str().c_str());
Mårten Kongstad02751232018-04-27 13:16:32 +0200111 }
112 }
113 opt.action(argv[i]);
114 mandatory_opts.erase(opt.name);
115 break;
116 }
117 }
118 if (!match) {
Mårten Kongstad0c6ff1d2019-02-07 02:21:56 +0100119 std::stringstream stream;
120 Usage(stream);
121 return Error("%s: unknown option\n%s", arg.c_str(), stream.str().c_str());
Mårten Kongstad02751232018-04-27 13:16:32 +0200122 }
123 }
124
125 if (!mandatory_opts.empty()) {
Mårten Kongstad0c6ff1d2019-02-07 02:21:56 +0100126 std::stringstream stream;
127 bool separator = false;
Mårten Kongstad0eba72a2018-11-29 08:23:14 +0100128 for (const auto& opt : mandatory_opts) {
Mårten Kongstad0c6ff1d2019-02-07 02:21:56 +0100129 if (separator) {
130 stream << ", ";
131 }
132 separator = true;
133 stream << opt << ": missing mandatory option";
Mårten Kongstad02751232018-04-27 13:16:32 +0200134 }
Yurii Zubrytskyia29f3c02023-05-12 16:05:13 -0700135 stream << '\n';
Mårten Kongstad0c6ff1d2019-02-07 02:21:56 +0100136 Usage(stream);
137 return Error("%s", stream.str().c_str());
Mårten Kongstad02751232018-04-27 13:16:32 +0200138 }
Mårten Kongstad0c6ff1d2019-02-07 02:21:56 +0100139 return Unit{};
Mårten Kongstad02751232018-04-27 13:16:32 +0200140}
141
142void CommandLineOptions::Usage(std::ostream& out) const {
143 size_t maxLength = 0;
144 out << "usage: " << name_;
145 for (const Option& opt : options_) {
Mårten Kongstadd10d06d2019-01-07 17:26:25 -0800146 const bool mandatory =
147 opt.count != Option::COUNT_OPTIONAL && opt.count != Option::COUNT_OPTIONAL_ONCE_OR_MORE;
Mårten Kongstad02751232018-04-27 13:16:32 +0200148 out << " ";
149 if (!mandatory) {
150 out << "[";
151 }
152 if (opt.argument) {
153 out << opt.name << " arg";
154 maxLength = std::max(maxLength, opt.name.size() + 4);
155 } else {
156 out << opt.name;
157 maxLength = std::max(maxLength, opt.name.size());
158 }
Mårten Kongstadd10d06d2019-01-07 17:26:25 -0800159
160 if (opt.count == Option::COUNT_OPTIONAL_ONCE_OR_MORE) {
161 out << " [..]";
162 }
163
Mårten Kongstad02751232018-04-27 13:16:32 +0200164 if (!mandatory) {
165 out << "]";
166 }
Mårten Kongstadd10d06d2019-01-07 17:26:25 -0800167
Mårten Kongstad02751232018-04-27 13:16:32 +0200168 if (opt.count == Option::COUNT_ONCE_OR_MORE) {
169 out << " [" << opt.name << " arg [..]]";
170 }
171 }
Yurii Zubrytskyia29f3c02023-05-12 16:05:13 -0700172 out << "\n\n";
Mårten Kongstad02751232018-04-27 13:16:32 +0200173 for (const Option& opt : options_) {
174 out << std::left << std::setw(maxLength);
175 if (opt.argument) {
176 out << (opt.name + " arg");
177 } else {
178 out << opt.name;
179 }
180 out << " " << opt.description;
Mårten Kongstadd10d06d2019-01-07 17:26:25 -0800181 if (opt.count == Option::COUNT_ONCE_OR_MORE ||
182 opt.count == Option::COUNT_OPTIONAL_ONCE_OR_MORE) {
Mårten Kongstad02751232018-04-27 13:16:32 +0200183 out << " (can be provided multiple times)";
184 }
Yurii Zubrytskyia29f3c02023-05-12 16:05:13 -0700185 out << '\n';
Mårten Kongstad02751232018-04-27 13:16:32 +0200186 }
187}
188
Mårten Kongstad0eba72a2018-11-29 08:23:14 +0100189} // namespace android::idmap2