Porytiles
Loading...
Searching...
No Matches
cli_option_provider_impl.ipp
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm>
4#include <cctype>
5#include <charconv>
6#include <cstdint>
7#include <exception>
8#include <format>
9#include <optional>
10#include <sstream>
11#include <string>
12#include <vector>
13
26
27// The anonymous namespace ensures internal linkage per translation unit
28// This file is intentionally included only in cli_option_provider.cpp
29namespace {
30
31using namespace porytiles;
32
42LayerValue<std::size_t> parse_size_t(const std::optional<std::string> &raw_value, const std::string &option_name)
43{
44 if (!raw_value.has_value()) {
46 }
47
48 const auto &str = raw_value.value();
49
50 // Use std::from_chars for efficient, locale-independent parsing
51 std::size_t value = 0;
52 const auto *begin = str.data();
53 const auto *end = str.data() + str.size();
54 auto [ptr, ec] = std::from_chars(begin, end, value);
55
56 if (ec == std::errc{} && ptr == end) {
57 return LayerValue<std::size_t>::valid(value, option_name, "CLI");
58 }
59
60 if (ec == std::errc::result_out_of_range) {
61 const auto error = std::format("Invalid value '{}' for '{}': value out of range.", str, option_name);
62 return LayerValue<std::size_t>::invalid(error, option_name);
63 }
64
65 // Invalid argument or trailing characters
66 const auto error = std::format("Invalid value '{}' for '{}': not a valid integer.", str, option_name);
67 return LayerValue<std::size_t>::invalid(error, option_name);
68}
69
81parse_optional_size_t(const std::optional<std::string> &raw_value, const std::string &option_name)
82{
83 if (!raw_value.has_value()) {
85 }
86
87 const auto &str = raw_value.value();
88
89 std::size_t value = 0;
90 const auto *begin = str.data();
91 const auto *end = str.data() + str.size();
92 auto [ptr, ec] = std::from_chars(begin, end, value);
93
94 if (ec == std::errc{} && ptr == end) {
95 return LayerValue<std::optional<std::size_t>>::valid(std::optional<std::size_t>{value}, option_name, "CLI");
96 }
97
98 if (ec == std::errc::result_out_of_range) {
99 const auto error = std::format("Invalid value '{}' for '{}': value out of range.", str, option_name);
101 }
102
103 const auto error = std::format("Invalid value '{}' for '{}': not a valid integer.", str, option_name);
105}
106
116LayerValue<bool> parse_bool(const std::optional<std::string> &raw_value, const std::string &option_name)
117{
118 if (!raw_value.has_value()) {
120 }
121
122 const auto &str = raw_value.value();
123
124 if (str == "true") {
125 return LayerValue<bool>::valid(true, option_name, "CLI");
126 }
127 if (str == "false") {
128 return LayerValue<bool>::valid(false, option_name, "CLI");
129 }
130
131 const auto error = std::format("Invalid value '{}' for '{}': expected 'true' or 'false'.", str, option_name);
132 return LayerValue<bool>::invalid(error, option_name);
133}
134
144LayerValue<Rgba32> parse_rgba32(const std::optional<std::string> &raw_value, const std::string &option_name)
145{
146 if (!raw_value.has_value()) {
148 }
149
150 const auto &input = raw_value.value();
151 std::istringstream iss{input};
152 std::string token;
153 std::vector<int> values;
154
155 while (std::getline(iss, token, ',')) {
156 // Use std::from_chars for robust integer parsing
157 int val = 0;
158 const auto *begin = token.data();
159 const auto *end = token.data() + token.size();
160 auto [ptr, ec] = std::from_chars(begin, end, val);
161
162 if (ec != std::errc{} || ptr != end) {
163 const auto error =
164 std::format("Invalid value '{}' for '{}': '{}' is not a valid integer.", input, option_name, token);
165 return LayerValue<Rgba32>::invalid(error, option_name);
166 }
167
168 if (val < 0 || val > 255) {
169 const auto error = std::format(
170 "Invalid value '{}' for '{}': component {} is out of range (must be 0-255).", input, option_name, val);
171 return LayerValue<Rgba32>::invalid(error, option_name);
172 }
173
174 values.push_back(val);
175 }
176
177 if (values.size() == 3) {
178 Rgba32 result{
179 static_cast<std::uint8_t>(values[0]),
180 static_cast<std::uint8_t>(values[1]),
181 static_cast<std::uint8_t>(values[2]),
182 255};
183 return LayerValue<Rgba32>::valid(result, option_name, "CLI");
184 }
185
186 if (values.size() == 4) {
187 Rgba32 result{
188 static_cast<std::uint8_t>(values[0]),
189 static_cast<std::uint8_t>(values[1]),
190 static_cast<std::uint8_t>(values[2]),
191 static_cast<std::uint8_t>(values[3])};
192 return LayerValue<Rgba32>::valid(result, option_name, "CLI");
193 }
194
195 const auto error = std::format(
196 "Invalid value '{}' for '{}': expected R,G,B or R,G,B,A format (got {} components).",
197 input,
198 option_name,
199 values.size());
200 return LayerValue<Rgba32>::invalid(error, option_name);
201}
202
211LayerValue<std::string> parse_string(const std::optional<std::string> &raw_value, const std::string &option_name)
212{
213 if (!raw_value.has_value()) {
215 }
216
217 return LayerValue<std::string>::valid(raw_value.value(), option_name, "CLI");
218}
219
233parse_string_vector(const std::vector<std::string> &raw_values, const std::string &option_name)
234{
235 if (raw_values.empty()) {
237 }
238 return LayerValue<std::vector<std::string>>::valid(raw_values, option_name, "CLI");
239}
240
252parse_artifact_edit_mode(const std::optional<std::string> &raw_value, const std::string &option_name)
253{
254 if (!raw_value.has_value()) {
256 }
257
258 const auto &str = raw_value.value();
259 const auto result = artifact_edit_mode_from_str(str);
260
261 if (result.has_value()) {
262 return LayerValue<ArtifactEditMode>::valid(result.value(), option_name, "CLI");
263 }
264
265 const auto error = std::format("Invalid value '{}' for '{}'.", str, option_name);
266 return LayerValue<ArtifactEditMode>::invalid(error, option_name);
267}
268
279parse_tiles_palette_mode(const std::optional<std::string> &raw_value, const std::string &option_name)
280{
281 if (!raw_value.has_value()) {
283 }
284
285 const auto &str = raw_value.value();
286 const auto result = tiles_palette_mode_from_str(str);
287
288 if (result.has_value()) {
289 return LayerValue<TilesPaletteMode>::valid(result.value(), option_name, "CLI");
290 }
291
292 const auto error = std::format("Invalid value '{}' for '{}'.", str, option_name);
293 return LayerValue<TilesPaletteMode>::invalid(error, option_name);
294}
295
306parse_anim_palette_resolution_strategy(const std::optional<std::string> &raw_value, const std::string &option_name)
307{
308 if (!raw_value.has_value()) {
310 }
311
312 const auto &str = raw_value.value();
313 const auto result = anim_palette_resolution_strategy_from_str(str);
314
315 if (result.has_value()) {
316 return LayerValue<AnimPaletteResolutionStrategy>::valid(result.value(), option_name, "CLI");
317 }
318
319 const auto error = std::format("Invalid value '{}' for '{}'.", str, option_name);
321}
322
333parse_anim_key_frame_resolution_strategy(const std::optional<std::string> &raw_value, const std::string &option_name)
334{
335 if (!raw_value.has_value()) {
337 }
338
339 const auto &str = raw_value.value();
340 const auto result = anim_key_frame_resolution_strategy_from_str(str);
341
342 if (result.has_value()) {
343 return LayerValue<AnimKeyFrameResolutionStrategy>::valid(result.value(), option_name, "CLI");
344 }
345
346 const auto error = std::format("Invalid value '{}' for '{}'.", str, option_name);
348}
349
359LayerValue<AnimMultiPaletteSubtileResolutionStrategy> parse_anim_multi_palette_subtile_resolution_strategy(
360 const std::optional<std::string> &raw_value, const std::string &option_name)
361{
362 if (!raw_value.has_value()) {
364 }
365
366 const auto &str = raw_value.value();
368
369 if (result.has_value()) {
370 return LayerValue<AnimMultiPaletteSubtileResolutionStrategy>::valid(result.value(), option_name, "CLI");
371 }
372
373 const auto error = std::format("Invalid value '{}' for '{}'.", str, option_name);
375}
376
387parse_frame_linking(const std::optional<std::string> &raw_value, const std::string &option_name)
388{
389 if (!raw_value.has_value()) {
391 }
392
393 const auto &str = raw_value.value();
394 const auto result = frame_linking_from_str(str);
395
396 if (result.has_value()) {
397 return LayerValue<FrameLinking>::valid(result.value(), option_name, "CLI");
398 }
399
400 const auto error = std::format("Invalid value '{}' for '{}'.", str, option_name);
401 return LayerValue<FrameLinking>::invalid(error, option_name);
402}
403
414parse_tile_sharing_packing(const std::optional<std::string> &raw_value, const std::string &option_name)
415{
416 if (!raw_value.has_value()) {
418 }
419
420 const auto &str = raw_value.value();
421 const auto result = tile_sharing_packing_from_str(str);
422
423 if (result.has_value()) {
424 return LayerValue<TileSharingPacking>::valid(result.value(), option_name, "CLI");
425 }
426
427 const auto error = std::format("Invalid value '{}' for '{}'.", str, option_name);
429}
430
441parse_tile_sharing_alignment(const std::optional<std::string> &raw_value, const std::string &option_name)
442{
443 if (!raw_value.has_value()) {
445 }
446
447 const auto &str = raw_value.value();
448 const auto result = tile_sharing_alignment_from_str(str);
449
450 if (result.has_value()) {
451 return LayerValue<TileSharingAlignment>::valid(result.value(), option_name, "CLI");
452 }
453
454 const auto error = std::format("Invalid value '{}' for '{}'.", str, option_name);
456}
457
459parse_packing_strategy_type(const std::optional<std::string> &raw_value, const std::string &option_name)
460{
461 if (!raw_value.has_value()) {
463 }
464
465 const auto &str = raw_value.value();
466 const auto result = packing_strategy_type_from_str(str);
467
468 if (result.has_value()) {
469 return LayerValue<PackingStrategyType>::valid(result.value(), option_name, "CLI");
470 }
471
472 const auto error = std::format("Invalid value '{}' for '{}'.", str, option_name);
474}
475
477parse_primary_pairing_mode(const std::optional<std::string> &raw_value, const std::string &option_name)
478{
479 if (!raw_value.has_value()) {
481 }
482
483 const auto &str = raw_value.value();
484 const auto result = primary_pairing_mode_from_str(str);
485
486 if (result.has_value()) {
487 return LayerValue<PrimaryPairingMode>::valid(result.value(), option_name, "CLI");
488 }
489
490 const auto error = std::format("Invalid value '{}' for '{}'.", str, option_name);
492}
493
494} // namespace
Represents a 32-bit RGBA color.
Definition rgba32.hpp:21
std::size_t end
std::optional< ArtifactEditMode > artifact_edit_mode_from_str(const std::string &str)
Parses a string into a ArtifactEditMode with fuzzy matching.
std::optional< PackingStrategyType > packing_strategy_type_from_str(const std::string &str)
Parses a string into a PackingStrategyType with fuzzy matching.
std::optional< FrameLinking > frame_linking_from_str(const std::string &str)
Parses a string into a FrameLinking with fuzzy matching.
std::optional< PrimaryPairingMode > primary_pairing_mode_from_str(const std::string &str)
Parses a string into a PrimaryPairingMode with fuzzy matching.
std::optional< AnimMultiPaletteSubtileResolutionStrategy > anim_multi_palette_subtile_resolution_strategy_from_str(const std::string &str)
Parses a string into a AnimMultiPaletteSubtileResolutionStrategy with fuzzy matching.
@ not_provided
nothing attribute-related was found; other providers should be consulted
@ valid
one or more usable candidate sets were inferred
@ invalid
no layout could be determined from what the project declares (fatal at resolution time)
@ error
Emit a formatted error and fail decompilation.
std::optional< TileSharingPacking > tile_sharing_packing_from_str(const std::string &str)
Parses a string into a TileSharingPacking with fuzzy matching.
std::optional< TileSharingAlignment > tile_sharing_alignment_from_str(const std::string &str)
Parses a string into a TileSharingAlignment with fuzzy matching.
std::optional< AnimPaletteResolutionStrategy > anim_palette_resolution_strategy_from_str(const std::string &str)
Parses a string into a AnimPaletteResolutionStrategy with fuzzy matching.
std::optional< TilesPaletteMode > tiles_palette_mode_from_str(const std::string &str)
Parses a string into a TilesPaletteMode with fuzzy matching.
std::optional< AnimKeyFrameResolutionStrategy > anim_key_frame_resolution_strategy_from_str(const std::string &str)
Parses a string into a AnimKeyFrameResolutionStrategy with fuzzy matching.
A small container that holds an optional-wrapped value, validation state, and metadata about the valu...
static LayerValue valid(T val, std::string source_key, std::string source_info)
Creates a LayerValue representing a valid configuration value.
static LayerValue not_provided()
Creates a LayerValue representing that the provider does not supply this configuration.
static LayerValue invalid(std::string error, std::string source_info)
Creates a LayerValue representing an invalid configuration value.