9#include "yaml-cpp/yaml.h"
22std::map<std::filesystem::path, YAML::Node> yaml_cache;
23std::map<std::filesystem::path, std::vector<std::string>> file_lines_cache;
36std::string get_line_content(
const std::filesystem::path &path, std::size_t line_num)
38 const auto it = file_lines_cache.find(path);
39 if (it != file_lines_cache.end() && line_num < it->second.size()) {
40 return it->second[line_num];
58std::string make_source_string(
const TextFormatter *format,
const std::string &file_path,
const YAML::Mark &mark)
88std::vector<std::string> make_source_details(
89 const TextFormatter *format,
const std::string &file_path,
const YAML::Mark &mark, std::size_t window_size = 7)
91 std::vector<std::string> details;
93 const std::filesystem::path path{file_path};
94 const auto it = file_lines_cache.find(path);
95 if (it == file_lines_cache.end()) {
99 const auto &lines = it->second;
100 const std::size_t line_num = mark.line;
102 if (lines.empty() || line_num >= lines.size()) {
107 const std::size_t half_window = (window_size - 1) / 2;
108 const std::size_t start = (line_num >= half_window) ? line_num - half_window : 0;
109 const std::size_t end = std::min(line_num + half_window + 1, lines.size());
112 for (std::size_t i = start; i < end; ++i) {
113 const auto formatted_arrow =
114 format->
format(
"{}",
FormatParam{
"-> ", Style::bold | Style::italic | Style::yellow});
115 const std::string prefix = (i == line_num) ? formatted_arrow :
" ";
120 const auto highlight_line =
121 format->
format(
"{}",
FormatParam{lines[i], Style::bold | Style::italic | Style::yellow});
122 details.push_back(prefix + std::to_string(i + 1) +
": " + highlight_line);
125 details.push_back(prefix + std::to_string(i + 1) +
": " + lines[i]);
142parse_size_t(
const TextFormatter *format,
const YAML::Node &node,
const std::string &key,
const std::string &file_path)
144 if (!node.IsDefined()) {
149 const auto value = node.as<std::size_t>();
150 const auto mark = node.Mark();
151 const auto source = make_source_string(format, file_path, mark);
152 const auto details = make_source_details(format, file_path, mark);
155 catch (
const YAML::Exception &e) {
156 const auto mark = node.Mark();
158 format->
format(
"failed to parse '{}' as std::size_t: {}",
FormatParam{key, Style::bold}, e.what());
159 const auto source = make_source_string(format, file_path, mark);
160 const auto details = make_source_details(format, file_path, mark);
175parse_bool(
const TextFormatter *format,
const YAML::Node &node,
const std::string &key,
const std::string &file_path)
177 if (!node.IsDefined()) {
182 const auto value = node.as<
bool>();
183 const auto mark = node.Mark();
184 const auto source = make_source_string(format, file_path, mark);
185 const auto details = make_source_details(format, file_path, mark);
188 catch (
const YAML::Exception &e) {
189 const auto mark = node.Mark();
190 const auto error = format->
format(
"failed to parse '{}' as bool: {}",
FormatParam{key, Style::bold}, e.what());
191 const auto source = make_source_string(format, file_path, mark);
192 const auto details = make_source_details(format, file_path, mark);
211parse_rgba32(
const TextFormatter *format,
const YAML::Node &node,
const std::string &key,
const std::string &file_path)
213 if (!node.IsDefined()) {
218 const auto mark = node.Mark();
219 const auto details = make_source_details(format, file_path, mark);
221 if (!node.IsSequence()) {
223 format->
format(
"'{}' must be a sequence [r, g, b] or [r, g, b, a]",
FormatParam{key, Style::bold});
224 const auto source = make_source_string(format, file_path, mark);
228 if (node.size() < 3 || node.size() > 4) {
229 const auto error = format->
format(
230 "'{}' must have 3 or 4 elements [r, g, b] or [r, g, b, a], got {}",
233 const auto source = make_source_string(format, file_path, mark);
237 const auto r = node[0].as<std::uint8_t>();
238 const auto g = node[1].as<std::uint8_t>();
239 const auto b = node[2].as<std::uint8_t>();
242 const Rgba32 color{r, g, b, a};
243 const auto source = make_source_string(format, file_path, mark);
246 catch (
const YAML::Exception &e) {
247 const auto mark = node.Mark();
249 format->
format(
"failed to parse '{}' as Rgba32: {}",
FormatParam{key, Style::bold}, e.what());
250 const auto source = make_source_string(format, file_path, mark);
251 const auto details = make_source_details(format, file_path, mark);
269 const TextFormatter *format,
const YAML::Node &node,
const std::string &key,
const std::string &file_path)
271 if (!node.IsDefined()) {
276 const auto mark = node.Mark();
277 const auto details = make_source_details(format, file_path, mark);
278 const auto str = node.as<std::string>();
281 if (!mode_opt.has_value()) {
282 const auto error = format->
format(
283 "'{}' has invalid value '{}', expected 'true-color' or 'greyscale'",
286 const auto source = make_source_string(format, file_path, mark);
290 const auto source = make_source_string(format, file_path, mark);
293 catch (
const YAML::Exception &e) {
294 const auto mark = node.Mark();
296 format->
format(
"failed to parse '{}' as TilesPalMode: {}",
FormatParam{key, Style::bold}, e.what());
297 const auto source = make_source_string(format, file_path, mark);
298 const auto details = make_source_details(format, file_path, mark);
313std::optional<YAML::Node> load_yaml_file(
const std::filesystem::path &path)
316 const auto cache_it = yaml_cache.find(path);
317 if (cache_it != yaml_cache.end()) {
318 return cache_it->second;
322 if (!std::filesystem::exists(path)) {
328 auto node = YAML::LoadFile(path.string());
329 yaml_cache[path] = node;
332 std::ifstream file{path};
333 std::vector<std::string> lines;
335 while (std::getline(file, line)) {
336 lines.push_back(line);
338 file_lines_cache[path] = std::move(lines);
342 catch (
const YAML::Exception &) {
363std::vector<std::filesystem::path> get_tileset_config_path_chain(
364 const std::filesystem::path &project_root,
366 const std::string &tileset)
373 std::vector<std::filesystem::path> paths;
382 paths.push_back(std::filesystem::path{tileset_local_config_key.key()});
385 paths.push_back(std::filesystem::path{tileset_config_key.key()});
388 paths.push_back(project_root /
"porytiles.local.yaml");
391 paths.push_back(project_root /
"porytiles.yaml");
420template <
typename T,
typename LoadFunc,
typename NodeExtractFunc,
typename ParseFunc>
423 const std::vector<std::filesystem::path> &paths,
425 NodeExtractFunc extract_node_func,
426 ParseFunc parse_func,
427 const std::string &key)
429 for (
const auto &path : paths) {
430 const auto yaml_doc = load_func(path);
431 if (!yaml_doc.has_value()) {
437 const auto node = extract_node_func(yaml_doc.value());
438 const auto result = parse_func(format, node, key, path.string());
441 if (result.state == ValidationState::valid || result.state == ValidationState::invalid) {
447 catch (
const YAML::Exception &) {
Represents a 32-bit RGBA color.
static constexpr std::uint8_t alpha_opaque
Abstract base class for applying text styling with context-aware formatting.
virtual std::string format(const std::string &format_str, const std::vector< FormatParam > ¶ms) const
Formats a string with styled parameters using fmtlib syntax.
Abstract interface for generating keys and discovering tileset artifacts in a backing store.
virtual ArtifactKey key_for(const std::string &tileset_name, const TilesetArtifact &artifact) const =0
Constructs a key for a given tileset artifact.
Represents a Pokémon Generation III decomp tileset artifact with type and optional metadata.
Type
Enumeration of all supported tileset artifact types.
std::optional< TilesPalMode > tiles_pal_mode_from_str(const std::string &str)
A small container that holds an optional-wrapped value, validation state, and metadata about the valu...
static LayerValue valid(T val, 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.