161 lines
6.5 KiB
C++
161 lines
6.5 KiB
C++
/*
|
|
* Copyright (C) 2021 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <cstdint>
|
|
#include <functional>
|
|
#include <optional>
|
|
#include <ostream>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
/* Support for parsing individual flags out of a larger list of flags. This
|
|
* supports externally determining the order that flags are evaluated in, and
|
|
* incrementally integrating with existing flag parsing implementations.
|
|
*
|
|
* Start with Flag() or one of the GflagsCompatFlag(...) functions to create new
|
|
* flags. These flags should be aggregated through the application through some
|
|
* other mechanism and then evaluated individually with Flag::Parse or together
|
|
* with ParseFlags on arguments. */
|
|
|
|
namespace cuttlefish {
|
|
|
|
/* The matching behavior used with the name in `FlagAlias::name`. */
|
|
enum class FlagAliasMode {
|
|
/* Match arguments of the form `<name><value>`. In practice, <name> usually
|
|
* looks like "-flag=" or "--flag=", where the "-" and "=" are included in
|
|
* parsing. */
|
|
kFlagPrefix,
|
|
/* Match arguments of the form `<name>`. In practice, <name> will look like
|
|
* "-flag" or "--flag". */
|
|
kFlagExact,
|
|
/* Match a pair of arguments of the form `<name>` `<value>`. In practice,
|
|
* <name> will look like "-flag" or "--flag". */
|
|
kFlagConsumesFollowing,
|
|
/* Match a sequence of arguments of the form `<name>` `<value>` `<value>`.
|
|
* This uses heuristics to try to determine when `<value>` is actually another
|
|
* flag. */
|
|
kFlagConsumesArbitrary,
|
|
};
|
|
|
|
/* A single matching rule for a `Flag`. One `Flag` can have multiple rules. */
|
|
struct FlagAlias {
|
|
FlagAliasMode mode;
|
|
std::string name;
|
|
};
|
|
|
|
std::ostream& operator<<(std::ostream&, const FlagAlias&);
|
|
|
|
/* A successful match in an argument list from a `FlagAlias` inside a `Flag`.
|
|
* The `key` value corresponds to `FlagAlias::name`. For a match of
|
|
* `FlagAliasMode::kFlagExact`, `key` and `value` will both be the `name`. */
|
|
struct FlagMatch {
|
|
std::string key;
|
|
std::string value;
|
|
};
|
|
|
|
class Flag {
|
|
public:
|
|
/* Add an alias that triggers matches and calls to the `Setter` function. */
|
|
Flag& Alias(const FlagAlias& alias) &;
|
|
Flag Alias(const FlagAlias& alias) &&;
|
|
/* Set help text, visible in the class ostream writer method. Optional. */
|
|
Flag& Help(const std::string&) &;
|
|
Flag Help(const std::string&) &&;
|
|
/* Set a loader that displays the current value in help text. Optional. */
|
|
Flag& Getter(std::function<std::string()>) &;
|
|
Flag Getter(std::function<std::string()>) &&;
|
|
/* Set the callback for matches. The callback be invoked multiple times. */
|
|
Flag& Setter(std::function<bool(const FlagMatch&)>) &;
|
|
Flag Setter(std::function<bool(const FlagMatch&)>) &&;
|
|
|
|
/* Examines a list of arguments, removing any matches from the list and
|
|
* invoking the `Setter` for every match. Returns `false` if the callback ever
|
|
* returns `false`. Non-matches are left in place. */
|
|
bool Parse(std::vector<std::string>& flags) const;
|
|
bool Parse(std::vector<std::string>&& flags) const;
|
|
|
|
/* Write gflags `--helpxml` style output for a string-type flag. */
|
|
bool WriteGflagsCompatXml(std::ostream&) const;
|
|
|
|
private:
|
|
/* Reports whether `Process` wants to consume zero, one, or two arguments. */
|
|
enum class FlagProcessResult {
|
|
/* Error in handling a flag, exit flag handling with an error result. */
|
|
kFlagError,
|
|
kFlagSkip, /* Flag skipped; consume no arguments. */
|
|
kFlagConsumed, /* Flag processed; consume one argument. */
|
|
kFlagConsumedWithFollowing, /* Flag processed; consume 2 arguments. */
|
|
kFlagConsumedOnlyFollowing, /* Flag processed; consume next argument. */
|
|
};
|
|
|
|
void ValidateAlias(const FlagAlias& alias);
|
|
Flag& UnvalidatedAlias(const FlagAlias& alias) &;
|
|
Flag UnvalidatedAlias(const FlagAlias& alias) &&;
|
|
|
|
/* Attempt to match a single argument. */
|
|
FlagProcessResult Process(const std::string& argument,
|
|
const std::optional<std::string>& next_arg) const;
|
|
|
|
bool HasAlias(const FlagAlias&) const;
|
|
|
|
friend std::ostream& operator<<(std::ostream&, const Flag&);
|
|
friend Flag InvalidFlagGuard();
|
|
friend Flag UnexpectedArgumentGuard();
|
|
|
|
std::vector<FlagAlias> aliases_;
|
|
std::optional<std::string> help_;
|
|
std::optional<std::function<std::string()>> getter_;
|
|
std::optional<std::function<bool(const FlagMatch&)>> setter_;
|
|
};
|
|
|
|
std::ostream& operator<<(std::ostream&, const Flag&);
|
|
|
|
std::vector<std::string> ArgsToVec(int argc, char** argv);
|
|
|
|
/* Handles a list of flags. Flags are matched in the order given in case two
|
|
* flags match the same argument. Matched flags are removed, leaving only
|
|
* unmatched arguments. */
|
|
bool ParseFlags(const std::vector<Flag>& flags, std::vector<std::string>& args);
|
|
bool ParseFlags(const std::vector<Flag>& flags, std::vector<std::string>&&);
|
|
|
|
bool WriteGflagsCompatXml(const std::vector<Flag>&, std::ostream&);
|
|
|
|
/* If any of these are used, they should be evaluated after all other flags, and
|
|
* in the order defined here (help before invalid flags, invalid flags before
|
|
* unexpected arguments). */
|
|
|
|
/* If a "-help" or "--help" flag is present, prints all the flags and fails. */
|
|
Flag HelpFlag(const std::vector<Flag>& flags, const std::string& text = "");
|
|
/* Catches unrecognized arguments that begin with `-`, and errors out. This
|
|
* effectively denies unknown flags. */
|
|
Flag InvalidFlagGuard();
|
|
/* Catches any arguments not extracted by other Flag matchers and errors out.
|
|
* This effectively denies unknown flags and any positional arguments. */
|
|
Flag UnexpectedArgumentGuard();
|
|
|
|
// Create a flag resembling a gflags argument of the given type. This includes
|
|
// "-[-]flag=*",support for all types, "-[-]noflag" support for booleans, and
|
|
// "-flag *", "--flag *", support for other types. The value passed in the flag
|
|
// is saved to the defined reference.
|
|
Flag GflagsCompatFlag(const std::string& name);
|
|
Flag GflagsCompatFlag(const std::string& name, std::string& value);
|
|
Flag GflagsCompatFlag(const std::string& name, std::int32_t& value);
|
|
Flag GflagsCompatFlag(const std::string& name, bool& value);
|
|
|
|
} // namespace cuttlefish
|