yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
global_flags.cc
Go to the documentation of this file.
1#include "cli/global_flags.h"
2
3#include <cstddef>
4#include <string>
5
6#include "absl/algorithm/container.h"
7#include "absl/flags/commandlineflag.h"
8#include "absl/flags/reflection.h"
9#include "absl/status/status.h"
10#include "absl/strings/match.h"
11#include "absl/strings/str_cat.h"
12#include "absl/strings/str_replace.h"
13#include "absl/strings/string_view.h"
14#include "absl/strings/strip.h"
15
16namespace yaze::cli {
17namespace {
18
19// absl flags z3ed accepts anywhere on the command line. Command-specific
20// flags such as --format are not listed and reach the command untouched.
21// A listed flag that is not linked into this build (the HTTP flags when the
22// HTTP API is compiled out) is forwarded to the command like any other token.
23constexpr absl::string_view kGlobalFlagNames[] = {
24 "rom",
25 "quiet",
26 "sandbox",
27 "ai_provider",
28 "ai_model",
29 "gemini_api_key",
30 "anthropic_api_key",
31 "openai_base_url",
32 "ollama_host",
33 "gui_server_address",
34 "mesen_socket",
35 "prompt_version",
36 "use_function_calling",
37 "http_port",
38 "http_host",
39 "http_allow_commands",
40};
41
42struct ActionFlag {
43 absl::string_view spelling;
45};
46
47constexpr ActionFlag kActionFlags[] = {
48 {"--version", &GlobalOptions::show_version},
50 {"--list-commands", &GlobalOptions::list_commands},
52 {"--export-schemas", &GlobalOptions::export_schemas},
53 {"--export_schemas", &GlobalOptions::export_schemas},
54 {"--self-test", &GlobalOptions::self_test},
55 {"--selftest", &GlobalOptions::self_test},
56};
57
58bool* FindActionFlag(absl::string_view token, GlobalOptions& options) {
59 for (const ActionFlag& action : kActionFlags) {
60 if (token == action.spelling) {
61 return &(options.*action.field);
62 }
63 }
64 return nullptr;
65}
66
67// A bare boolean flag may be followed by an explicit value, as in
68// "--use_function_calling false"; any other next token is a positional.
69bool IsBoolLiteral(absl::string_view token) {
70 return token == "true" || token == "false" || token == "1" || token == "0";
71}
72
73// Applies args[index] if it is a global flag. Returns the number of tokens
74// consumed, which is 0 when args[index] is not a global flag.
75absl::StatusOr<size_t> ApplyGlobalFlag(absl::Span<const std::string> args,
76 size_t index) {
77 absl::string_view token = args[index];
78 if (token == "-q") {
79 token = "--quiet";
80 }
81 if (!absl::ConsumePrefix(&token, "--")) {
82 return 0;
83 }
84
85 const size_t equals = token.find('=');
86 const absl::string_view spelled_name = token.substr(0, equals);
87 const std::string name = absl::StrReplaceAll(spelled_name, {{"-", "_"}});
88 if (!absl::c_linear_search(kGlobalFlagNames, name)) {
89 return 0;
90 }
91 absl::CommandLineFlag* flag = absl::FindCommandLineFlag(name);
92 if (flag == nullptr) {
93 return 0;
94 }
95
96 size_t consumed = 1;
97 std::string value;
98 if (equals != absl::string_view::npos) {
99 value = std::string(token.substr(equals + 1));
100 } else if (flag->IsOfType<bool>()) {
101 value = "true";
102 if (index + 1 < args.size() && IsBoolLiteral(args[index + 1])) {
103 value = args[index + 1];
104 consumed = 2;
105 }
106 } else if (index + 1 < args.size()) {
107 value = args[index + 1];
108 consumed = 2;
109 } else {
110 return absl::InvalidArgumentError(
111 absl::StrCat("--", spelled_name, " flag requires a value"));
112 }
113
114 std::string error;
115 if (!flag->ParseFrom(value, &error)) {
116 return absl::InvalidArgumentError(
117 absl::StrCat("Invalid value for --", spelled_name, ": ", error));
118 }
119 return consumed;
120}
121
122} // namespace
123
124absl::StatusOr<GlobalOptions> ParseGlobalFlags(
125 absl::Span<const std::string> args) {
126 GlobalOptions options;
127 options.positional.reserve(args.size());
128
129 for (size_t i = 0; i < args.size(); ++i) {
130 const std::string& token = args[i];
131
132 if (token == "--") {
133 options.positional.insert(options.positional.end(), args.begin() + i + 1,
134 args.end());
135 break;
136 }
137
138 if (token == "--help" || token == "-h") {
139 if (i + 1 < args.size() && !absl::StartsWith(args[i + 1], "-")) {
140 options.help_target = args[++i];
141 } else {
142 options.show_help = true;
143 }
144 continue;
145 }
146 if (absl::StartsWith(token, "--help=")) {
147 std::string target = token.substr(7);
148 if (target.empty()) {
149 options.show_help = true;
150 } else {
151 options.help_target = std::move(target);
152 }
153 continue;
154 }
155
156 if (bool* action = FindActionFlag(token, options)) {
157 *action = true;
158 continue;
159 }
160
161 if (token == "--tui" || token == "--interactive") {
162 return absl::InvalidArgumentError(
163 "--tui/--interactive was removed; use `z3ed help` for CLI "
164 "workflows");
165 }
166
167 absl::StatusOr<size_t> consumed = ApplyGlobalFlag(args, i);
168 if (!consumed.ok()) {
169 return consumed.status();
170 }
171 if (*consumed > 0) {
172 i += *consumed - 1;
173 continue;
174 }
175
176 options.positional.push_back(token);
177 }
178
179 return options;
180}
181
182} // namespace yaze::cli
bool * FindActionFlag(absl::string_view token, GlobalOptions &options)
bool IsBoolLiteral(absl::string_view token)
absl::StatusOr< size_t > ApplyGlobalFlag(absl::Span< const std::string > args, size_t index)
constexpr absl::string_view kGlobalFlagNames[]
absl::StatusOr< GlobalOptions > ParseGlobalFlags(absl::Span< const std::string > args)
std::vector< std::string > positional
std::optional< std::string > help_target
bool equals(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, BinaryPredicate p)