Porytiles
Loading...
Searching...
No Matches
command_list_tilesets.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <filesystem>
4#include <iostream>
5#include <string>
6
7#include "CLI/CLI.hpp"
8
12
13#include "command.hpp"
14
28class ListTilesetsCommand final : public Command {
29 public:
30 explicit ListTilesetsCommand(CLI::App &parent_app) : Command{parent_app, command_name, command_desc, command_group}
31 {
32 CLI::App &cmd = get_app();
33
34 cmd.add_option("-C,--project-root", project_root_, "Project root directory")
35 ->default_val(".")
36 ->capture_default_str();
37
38 cmd.add_option("--filter", filter_, "Filter mode: all, managed, or unmanaged")
39 ->default_val("all")
40 ->check(CLI::IsMember({"all", "managed", "unmanaged"}))
41 ->capture_default_str();
42
43 cmd.add_option("--prefix", prefix_, "Only show tilesets starting with this prefix")->default_val("");
44 }
45
46 private:
47 void Run() override
48 {
49 using namespace porytiles;
50
51 // Validate project root exists - check early before creating objects
52 const std::filesystem::path project_path{project_root_};
53 if (!exists(project_path) || !is_directory(project_path)) {
54 // Silent exit - no output on errors
55 return;
56 }
57
58 // Use PlainTextFormatter (no colors needed for completion output)
59 PlainTextFormatter formatter{};
60 NullUserDiagnostics diag{&formatter};
61
62 // Get all tileset names from the project
63 ProjectTilesetMetadataProvider provider{project_path, &formatter, &diag};
64 auto result = provider.tilesets();
65
66 if (!result.has_value()) {
67 // Silent exit - no output on errors
68 return;
69 }
70
71 const auto &tileset_names = result.value();
72
73 // Output matching tileset names
74 for (const auto &name : tileset_names) {
75 // Apply prefix filter
76 if (!prefix_.empty() && !name.starts_with(prefix_)) {
77 continue;
78 }
79
80 // Apply management status filter
81 if (filter_ == "managed") {
82 if (!is_managed_tileset(project_path, name)) {
83 continue;
84 }
85 }
86 else if (filter_ == "unmanaged") {
87 if (is_managed_tileset(project_path, name)) {
88 continue;
89 }
90 }
91 // filter_ == "all" means no filtering
92
93 std::cout << name << "\n";
94 }
95 }
96
97 static constexpr auto command_name = "list-tilesets";
98 static constexpr auto command_desc = "List tilesets in the project.";
99 static constexpr auto command_group = "UTILITIES";
100
101 std::string project_root_;
102 std::string filter_;
103 std::string prefix_;
104
119 [[nodiscard]] static bool
120 is_managed_tileset(const std::filesystem::path &project_root, const std::string &tileset_name)
121 {
122 // Security: Validate tileset_name doesn't contain path traversal sequences.
123 // Tileset names from ProjectTilesetMetadataProvider should be safe, but validate anyway.
124 if (tileset_name.empty() || tileset_name.find('/') != std::string::npos ||
125 tileset_name.find('\\') != std::string::npos || tileset_name.find("..") != std::string::npos) {
126 return false;
127 }
128
129 // Check for porytiles manifest file using full tileset name as directory
130 const auto manifest_path = project_root / "porytiles" / "tilesets" / tileset_name / "tileset-manifest.json";
131
132 return exists(manifest_path);
133 }
134};
Command is an abstract class that provides basic command functionality for the Porytiles CLI driver.
Definition command.hpp:16
CLI::App & get_app() const
Definition command.hpp:42
Lists tileset names in the project.
ListTilesetsCommand(CLI::App &parent_app)
Silent diagnostics implementation that suppresses all output.
TextFormatter implementation that strips all styling from text.
Provides a pokeemerald project filesystem-based implementation for TilesetMetadataProvider.
ChainableResult< std::set< std::string > > tilesets() const override
Returns all tileset names known to this provider.
std::string name
std::optional< ProviderDefinition > provider