blob: b45c5445f6f5409af9d92d1479411af11109da0b [file] [log] [blame]
/*
* 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