1*61c4878aSAndroid Build Coastguard Worker // Copyright 2023 The Pigweed Authors
2*61c4878aSAndroid Build Coastguard Worker //
3*61c4878aSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4*61c4878aSAndroid Build Coastguard Worker // use this file except in compliance with the License. You may obtain a copy of
5*61c4878aSAndroid Build Coastguard Worker // the License at
6*61c4878aSAndroid Build Coastguard Worker //
7*61c4878aSAndroid Build Coastguard Worker // https://www.apache.org/licenses/LICENSE-2.0
8*61c4878aSAndroid Build Coastguard Worker //
9*61c4878aSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*61c4878aSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11*61c4878aSAndroid Build Coastguard Worker // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12*61c4878aSAndroid Build Coastguard Worker // License for the specific language governing permissions and limitations under
13*61c4878aSAndroid Build Coastguard Worker // the License.
14*61c4878aSAndroid Build Coastguard Worker
15*61c4878aSAndroid Build Coastguard Worker #include "pw_rpc/fuzz/argparse.h"
16*61c4878aSAndroid Build Coastguard Worker
17*61c4878aSAndroid Build Coastguard Worker #include <cctype>
18*61c4878aSAndroid Build Coastguard Worker #include <cstring>
19*61c4878aSAndroid Build Coastguard Worker
20*61c4878aSAndroid Build Coastguard Worker #include "pw_assert/check.h"
21*61c4878aSAndroid Build Coastguard Worker #include "pw_log/log.h"
22*61c4878aSAndroid Build Coastguard Worker #include "pw_string/string_builder.h"
23*61c4878aSAndroid Build Coastguard Worker
24*61c4878aSAndroid Build Coastguard Worker namespace pw::rpc::fuzz {
25*61c4878aSAndroid Build Coastguard Worker namespace {
26*61c4878aSAndroid Build Coastguard Worker
27*61c4878aSAndroid Build Coastguard Worker // Visitor to `ArgVariant` used by `ParseArgs` below.
28*61c4878aSAndroid Build Coastguard Worker struct ParseVisitor {
29*61c4878aSAndroid Build Coastguard Worker std::string_view arg0;
30*61c4878aSAndroid Build Coastguard Worker std::string_view arg1;
31*61c4878aSAndroid Build Coastguard Worker
32*61c4878aSAndroid Build Coastguard Worker template <typename Parser>
operator ()pw::rpc::fuzz::__anon5e30d65c0111::ParseVisitor33*61c4878aSAndroid Build Coastguard Worker ParseStatus operator()(Parser& parser) {
34*61c4878aSAndroid Build Coastguard Worker return parser.Parse(arg0, arg1);
35*61c4878aSAndroid Build Coastguard Worker }
36*61c4878aSAndroid Build Coastguard Worker };
37*61c4878aSAndroid Build Coastguard Worker
38*61c4878aSAndroid Build Coastguard Worker // Visitor to `ArgVariant` used by `GetArg` below.
39*61c4878aSAndroid Build Coastguard Worker struct ValueVisitor {
40*61c4878aSAndroid Build Coastguard Worker std::string_view name;
41*61c4878aSAndroid Build Coastguard Worker
42*61c4878aSAndroid Build Coastguard Worker template <typename Parser>
operator ()pw::rpc::fuzz::__anon5e30d65c0111::ValueVisitor43*61c4878aSAndroid Build Coastguard Worker std::optional<ArgVariant> operator()(Parser& parser) {
44*61c4878aSAndroid Build Coastguard Worker std::optional<ArgVariant> result;
45*61c4878aSAndroid Build Coastguard Worker if (parser.short_name() == name || parser.long_name() == name) {
46*61c4878aSAndroid Build Coastguard Worker result.emplace(parser.value());
47*61c4878aSAndroid Build Coastguard Worker }
48*61c4878aSAndroid Build Coastguard Worker return result;
49*61c4878aSAndroid Build Coastguard Worker }
50*61c4878aSAndroid Build Coastguard Worker };
51*61c4878aSAndroid Build Coastguard Worker
52*61c4878aSAndroid Build Coastguard Worker // Visitor to `ArgVariant` used by `PrintUsage` below.
53*61c4878aSAndroid Build Coastguard Worker const size_t kMaxUsageLen = 256;
54*61c4878aSAndroid Build Coastguard Worker struct UsageVisitor {
55*61c4878aSAndroid Build Coastguard Worker StringBuffer<kMaxUsageLen>* buffer;
56*61c4878aSAndroid Build Coastguard Worker
operator ()pw::rpc::fuzz::__anon5e30d65c0111::UsageVisitor57*61c4878aSAndroid Build Coastguard Worker void operator()(const BoolParser& parser) const {
58*61c4878aSAndroid Build Coastguard Worker auto short_name = parser.short_name();
59*61c4878aSAndroid Build Coastguard Worker auto long_name = parser.long_name();
60*61c4878aSAndroid Build Coastguard Worker *buffer << " [" << short_name << "|--[no-]" << long_name.substr(2) << "]";
61*61c4878aSAndroid Build Coastguard Worker }
62*61c4878aSAndroid Build Coastguard Worker
63*61c4878aSAndroid Build Coastguard Worker template <typename T>
operator ()pw::rpc::fuzz::__anon5e30d65c0111::UsageVisitor64*61c4878aSAndroid Build Coastguard Worker void operator()(const UnsignedParser<T>& parser) const {
65*61c4878aSAndroid Build Coastguard Worker auto short_name = parser.short_name();
66*61c4878aSAndroid Build Coastguard Worker auto long_name = parser.long_name();
67*61c4878aSAndroid Build Coastguard Worker *buffer << " ";
68*61c4878aSAndroid Build Coastguard Worker if (!parser.positional()) {
69*61c4878aSAndroid Build Coastguard Worker *buffer << "[";
70*61c4878aSAndroid Build Coastguard Worker if (!short_name.empty()) {
71*61c4878aSAndroid Build Coastguard Worker *buffer << short_name << "|";
72*61c4878aSAndroid Build Coastguard Worker }
73*61c4878aSAndroid Build Coastguard Worker *buffer << long_name << " ";
74*61c4878aSAndroid Build Coastguard Worker }
75*61c4878aSAndroid Build Coastguard Worker for (const auto& c : long_name) {
76*61c4878aSAndroid Build Coastguard Worker *buffer << static_cast<char>(toupper(c));
77*61c4878aSAndroid Build Coastguard Worker }
78*61c4878aSAndroid Build Coastguard Worker if (!parser.positional()) {
79*61c4878aSAndroid Build Coastguard Worker *buffer << "]";
80*61c4878aSAndroid Build Coastguard Worker }
81*61c4878aSAndroid Build Coastguard Worker }
82*61c4878aSAndroid Build Coastguard Worker };
83*61c4878aSAndroid Build Coastguard Worker
84*61c4878aSAndroid Build Coastguard Worker // Visitor to `ArgVariant` used by `ResetArg` below.
85*61c4878aSAndroid Build Coastguard Worker struct ResetVisitor {
86*61c4878aSAndroid Build Coastguard Worker std::string_view name;
87*61c4878aSAndroid Build Coastguard Worker
88*61c4878aSAndroid Build Coastguard Worker template <typename Parser>
operator ()pw::rpc::fuzz::__anon5e30d65c0111::ResetVisitor89*61c4878aSAndroid Build Coastguard Worker bool operator()(Parser& parser) {
90*61c4878aSAndroid Build Coastguard Worker if (parser.short_name() != name && parser.long_name() != name) {
91*61c4878aSAndroid Build Coastguard Worker return false;
92*61c4878aSAndroid Build Coastguard Worker }
93*61c4878aSAndroid Build Coastguard Worker parser.Reset();
94*61c4878aSAndroid Build Coastguard Worker return true;
95*61c4878aSAndroid Build Coastguard Worker }
96*61c4878aSAndroid Build Coastguard Worker };
97*61c4878aSAndroid Build Coastguard Worker
98*61c4878aSAndroid Build Coastguard Worker } // namespace
99*61c4878aSAndroid Build Coastguard Worker
ArgParserBase(std::string_view name)100*61c4878aSAndroid Build Coastguard Worker ArgParserBase::ArgParserBase(std::string_view name) : long_name_(name) {
101*61c4878aSAndroid Build Coastguard Worker PW_CHECK(!name.empty());
102*61c4878aSAndroid Build Coastguard Worker PW_CHECK(name != "--");
103*61c4878aSAndroid Build Coastguard Worker positional_ =
104*61c4878aSAndroid Build Coastguard Worker name[0] != '-' || (name.size() > 2 && name.substr(0, 2) != "--");
105*61c4878aSAndroid Build Coastguard Worker }
106*61c4878aSAndroid Build Coastguard Worker
ArgParserBase(std::string_view shortopt,std::string_view longopt)107*61c4878aSAndroid Build Coastguard Worker ArgParserBase::ArgParserBase(std::string_view shortopt,
108*61c4878aSAndroid Build Coastguard Worker std::string_view longopt)
109*61c4878aSAndroid Build Coastguard Worker : short_name_(shortopt), long_name_(longopt) {
110*61c4878aSAndroid Build Coastguard Worker PW_CHECK(shortopt.size() == 2);
111*61c4878aSAndroid Build Coastguard Worker PW_CHECK(shortopt[0] == '-');
112*61c4878aSAndroid Build Coastguard Worker PW_CHECK(shortopt != "--");
113*61c4878aSAndroid Build Coastguard Worker PW_CHECK(longopt.size() > 2);
114*61c4878aSAndroid Build Coastguard Worker PW_CHECK(longopt.substr(0, 2) == "--");
115*61c4878aSAndroid Build Coastguard Worker positional_ = false;
116*61c4878aSAndroid Build Coastguard Worker }
117*61c4878aSAndroid Build Coastguard Worker
Match(std::string_view arg)118*61c4878aSAndroid Build Coastguard Worker bool ArgParserBase::Match(std::string_view arg) {
119*61c4878aSAndroid Build Coastguard Worker if (arg.empty()) {
120*61c4878aSAndroid Build Coastguard Worker return false;
121*61c4878aSAndroid Build Coastguard Worker }
122*61c4878aSAndroid Build Coastguard Worker if (!positional_) {
123*61c4878aSAndroid Build Coastguard Worker return arg == short_name_ || arg == long_name_;
124*61c4878aSAndroid Build Coastguard Worker }
125*61c4878aSAndroid Build Coastguard Worker if (!std::holds_alternative<std::monostate>(value_)) {
126*61c4878aSAndroid Build Coastguard Worker return false;
127*61c4878aSAndroid Build Coastguard Worker }
128*61c4878aSAndroid Build Coastguard Worker if ((arg.size() == 2 && arg[0] == '-') ||
129*61c4878aSAndroid Build Coastguard Worker (arg.size() > 2 && arg.substr(0, 2) == "--")) {
130*61c4878aSAndroid Build Coastguard Worker PW_LOG_WARN("Argument parsed for '%s' appears to be a flag: '%s'",
131*61c4878aSAndroid Build Coastguard Worker long_name_.data(),
132*61c4878aSAndroid Build Coastguard Worker arg.data());
133*61c4878aSAndroid Build Coastguard Worker }
134*61c4878aSAndroid Build Coastguard Worker return true;
135*61c4878aSAndroid Build Coastguard Worker }
136*61c4878aSAndroid Build Coastguard Worker
GetValue() const137*61c4878aSAndroid Build Coastguard Worker const ArgVariant& ArgParserBase::GetValue() const {
138*61c4878aSAndroid Build Coastguard Worker return std::holds_alternative<std::monostate>(value_) ? initial_ : value_;
139*61c4878aSAndroid Build Coastguard Worker }
140*61c4878aSAndroid Build Coastguard Worker
BoolParser(std::string_view name)141*61c4878aSAndroid Build Coastguard Worker BoolParser::BoolParser(std::string_view name) : ArgParserBase(name) {}
BoolParser(std::string_view shortopt,std::string_view longopt)142*61c4878aSAndroid Build Coastguard Worker BoolParser::BoolParser(std::string_view shortopt, std::string_view longopt)
143*61c4878aSAndroid Build Coastguard Worker : ArgParserBase(shortopt, longopt) {}
144*61c4878aSAndroid Build Coastguard Worker
set_default(bool value)145*61c4878aSAndroid Build Coastguard Worker BoolParser& BoolParser::set_default(bool value) {
146*61c4878aSAndroid Build Coastguard Worker set_initial(value);
147*61c4878aSAndroid Build Coastguard Worker return *this;
148*61c4878aSAndroid Build Coastguard Worker }
149*61c4878aSAndroid Build Coastguard Worker
Parse(std::string_view arg0,std::string_view arg1)150*61c4878aSAndroid Build Coastguard Worker ParseStatus BoolParser::Parse(std::string_view arg0,
151*61c4878aSAndroid Build Coastguard Worker [[maybe_unused]] std::string_view arg1) {
152*61c4878aSAndroid Build Coastguard Worker if (Match(arg0)) {
153*61c4878aSAndroid Build Coastguard Worker set_value(true);
154*61c4878aSAndroid Build Coastguard Worker return kParsedOne;
155*61c4878aSAndroid Build Coastguard Worker }
156*61c4878aSAndroid Build Coastguard Worker if (arg0.size() > 5 && arg0.substr(0, 5) == "--no-" &&
157*61c4878aSAndroid Build Coastguard Worker arg0.substr(5) == long_name().substr(2)) {
158*61c4878aSAndroid Build Coastguard Worker set_value(false);
159*61c4878aSAndroid Build Coastguard Worker return kParsedOne;
160*61c4878aSAndroid Build Coastguard Worker }
161*61c4878aSAndroid Build Coastguard Worker return kParseMismatch;
162*61c4878aSAndroid Build Coastguard Worker }
163*61c4878aSAndroid Build Coastguard Worker
UnsignedParserBase(std::string_view name)164*61c4878aSAndroid Build Coastguard Worker UnsignedParserBase::UnsignedParserBase(std::string_view name)
165*61c4878aSAndroid Build Coastguard Worker : ArgParserBase(name) {}
UnsignedParserBase(std::string_view shortopt,std::string_view longopt)166*61c4878aSAndroid Build Coastguard Worker UnsignedParserBase::UnsignedParserBase(std::string_view shortopt,
167*61c4878aSAndroid Build Coastguard Worker std::string_view longopt)
168*61c4878aSAndroid Build Coastguard Worker : ArgParserBase(shortopt, longopt) {}
169*61c4878aSAndroid Build Coastguard Worker
Parse(std::string_view arg0,std::string_view arg1,uint64_t max)170*61c4878aSAndroid Build Coastguard Worker ParseStatus UnsignedParserBase::Parse(std::string_view arg0,
171*61c4878aSAndroid Build Coastguard Worker std::string_view arg1,
172*61c4878aSAndroid Build Coastguard Worker uint64_t max) {
173*61c4878aSAndroid Build Coastguard Worker auto result = kParsedOne;
174*61c4878aSAndroid Build Coastguard Worker if (!Match(arg0)) {
175*61c4878aSAndroid Build Coastguard Worker return kParseMismatch;
176*61c4878aSAndroid Build Coastguard Worker }
177*61c4878aSAndroid Build Coastguard Worker if (!positional()) {
178*61c4878aSAndroid Build Coastguard Worker if (arg1.empty()) {
179*61c4878aSAndroid Build Coastguard Worker PW_LOG_ERROR("Missing value for flag '%s'", arg0.data());
180*61c4878aSAndroid Build Coastguard Worker return kParseFailure;
181*61c4878aSAndroid Build Coastguard Worker }
182*61c4878aSAndroid Build Coastguard Worker arg0 = arg1;
183*61c4878aSAndroid Build Coastguard Worker result = kParsedTwo;
184*61c4878aSAndroid Build Coastguard Worker }
185*61c4878aSAndroid Build Coastguard Worker char* endptr;
186*61c4878aSAndroid Build Coastguard Worker unsigned long long value = strtoull(arg0.data(), &endptr, 0);
187*61c4878aSAndroid Build Coastguard Worker if (*endptr) {
188*61c4878aSAndroid Build Coastguard Worker PW_LOG_ERROR("Failed to parse number from '%s'", arg0.data());
189*61c4878aSAndroid Build Coastguard Worker return kParseFailure;
190*61c4878aSAndroid Build Coastguard Worker }
191*61c4878aSAndroid Build Coastguard Worker if (value > max) {
192*61c4878aSAndroid Build Coastguard Worker PW_LOG_ERROR("Parsed value is too large: %llu", value);
193*61c4878aSAndroid Build Coastguard Worker return kParseFailure;
194*61c4878aSAndroid Build Coastguard Worker }
195*61c4878aSAndroid Build Coastguard Worker set_value(static_cast<uint64_t>(value));
196*61c4878aSAndroid Build Coastguard Worker return result;
197*61c4878aSAndroid Build Coastguard Worker }
198*61c4878aSAndroid Build Coastguard Worker
ParseArgs(Vector<ArgParserVariant> & parsers,int argc,char ** argv)199*61c4878aSAndroid Build Coastguard Worker Status ParseArgs(Vector<ArgParserVariant>& parsers, int argc, char** argv) {
200*61c4878aSAndroid Build Coastguard Worker for (int i = 1; i < argc; ++i) {
201*61c4878aSAndroid Build Coastguard Worker auto arg0 = std::string_view(argv[i]);
202*61c4878aSAndroid Build Coastguard Worker auto arg1 =
203*61c4878aSAndroid Build Coastguard Worker i == (argc - 1) ? std::string_view() : std::string_view(argv[i + 1]);
204*61c4878aSAndroid Build Coastguard Worker bool parsed = false;
205*61c4878aSAndroid Build Coastguard Worker for (auto& parser : parsers) {
206*61c4878aSAndroid Build Coastguard Worker switch (std::visit(ParseVisitor{.arg0 = arg0, .arg1 = arg1}, parser)) {
207*61c4878aSAndroid Build Coastguard Worker case kParsedOne:
208*61c4878aSAndroid Build Coastguard Worker break;
209*61c4878aSAndroid Build Coastguard Worker case kParsedTwo:
210*61c4878aSAndroid Build Coastguard Worker ++i;
211*61c4878aSAndroid Build Coastguard Worker break;
212*61c4878aSAndroid Build Coastguard Worker case kParseMismatch:
213*61c4878aSAndroid Build Coastguard Worker continue;
214*61c4878aSAndroid Build Coastguard Worker case kParseFailure:
215*61c4878aSAndroid Build Coastguard Worker PW_LOG_ERROR("Failed to parse '%s'", arg0.data());
216*61c4878aSAndroid Build Coastguard Worker return Status::InvalidArgument();
217*61c4878aSAndroid Build Coastguard Worker }
218*61c4878aSAndroid Build Coastguard Worker parsed = true;
219*61c4878aSAndroid Build Coastguard Worker break;
220*61c4878aSAndroid Build Coastguard Worker }
221*61c4878aSAndroid Build Coastguard Worker if (!parsed) {
222*61c4878aSAndroid Build Coastguard Worker PW_LOG_ERROR("Unrecognized argument: '%s'", arg0.data());
223*61c4878aSAndroid Build Coastguard Worker return Status::InvalidArgument();
224*61c4878aSAndroid Build Coastguard Worker }
225*61c4878aSAndroid Build Coastguard Worker }
226*61c4878aSAndroid Build Coastguard Worker return OkStatus();
227*61c4878aSAndroid Build Coastguard Worker }
228*61c4878aSAndroid Build Coastguard Worker
PrintUsage(const Vector<ArgParserVariant> & parsers,std::string_view argv0)229*61c4878aSAndroid Build Coastguard Worker void PrintUsage(const Vector<ArgParserVariant>& parsers,
230*61c4878aSAndroid Build Coastguard Worker std::string_view argv0) {
231*61c4878aSAndroid Build Coastguard Worker StringBuffer<kMaxUsageLen> buffer;
232*61c4878aSAndroid Build Coastguard Worker buffer << "usage: " << argv0;
233*61c4878aSAndroid Build Coastguard Worker for (auto& parser : parsers) {
234*61c4878aSAndroid Build Coastguard Worker std::visit(UsageVisitor{.buffer = &buffer}, parser);
235*61c4878aSAndroid Build Coastguard Worker }
236*61c4878aSAndroid Build Coastguard Worker PW_LOG_INFO("%s", buffer.c_str());
237*61c4878aSAndroid Build Coastguard Worker }
238*61c4878aSAndroid Build Coastguard Worker
GetArg(const Vector<ArgParserVariant> & parsers,std::string_view name)239*61c4878aSAndroid Build Coastguard Worker std::optional<ArgVariant> GetArg(const Vector<ArgParserVariant>& parsers,
240*61c4878aSAndroid Build Coastguard Worker std::string_view name) {
241*61c4878aSAndroid Build Coastguard Worker for (auto& parser : parsers) {
242*61c4878aSAndroid Build Coastguard Worker if (auto result = std::visit(ValueVisitor{.name = name}, parser);
243*61c4878aSAndroid Build Coastguard Worker result.has_value()) {
244*61c4878aSAndroid Build Coastguard Worker return result;
245*61c4878aSAndroid Build Coastguard Worker }
246*61c4878aSAndroid Build Coastguard Worker }
247*61c4878aSAndroid Build Coastguard Worker return std::optional<ArgVariant>();
248*61c4878aSAndroid Build Coastguard Worker }
249*61c4878aSAndroid Build Coastguard Worker
ResetArg(Vector<ArgParserVariant> & parsers,std::string_view name)250*61c4878aSAndroid Build Coastguard Worker Status ResetArg(Vector<ArgParserVariant>& parsers, std::string_view name) {
251*61c4878aSAndroid Build Coastguard Worker for (auto& parser : parsers) {
252*61c4878aSAndroid Build Coastguard Worker if (std::visit(ResetVisitor{.name = name}, parser)) {
253*61c4878aSAndroid Build Coastguard Worker return OkStatus();
254*61c4878aSAndroid Build Coastguard Worker }
255*61c4878aSAndroid Build Coastguard Worker }
256*61c4878aSAndroid Build Coastguard Worker return Status::InvalidArgument();
257*61c4878aSAndroid Build Coastguard Worker }
258*61c4878aSAndroid Build Coastguard Worker
259*61c4878aSAndroid Build Coastguard Worker } // namespace pw::rpc::fuzz
260