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
30class ListTilesetsCommand final : public Command {
31 public:
32 explicit ListTilesetsCommand(CLI::App &parent_app) : Command{parent_app, kCommandName, kCommandDesc, kCommandGroup}
33 {
34 CLI::App &cmd = get_app();
35
36 cmd.add_option("-C,--project-root", project_root_, "Project root directory")
37 ->default_val(".")
38 ->capture_default_str();
39
40 cmd.add_option("--filter", filter_, "Filter mode: all, managed, or unmanaged")
41 ->default_val("all")
42 ->check(CLI::IsMember({"all", "managed", "unmanaged"}))
43 ->capture_default_str();
44
45 cmd.add_option("--prefix", prefix_, "Only show tilesets starting with this prefix")->default_val("");
46 }
47
48 void Run() override
49 {
50 using namespace porytiles;
51
52 // Validate project root exists - check early before creating objects
53 const std::filesystem::path project_path{project_root_};
54 if (!exists(project_path) || !is_directory(project_path)) {
55 // Silent exit - no output on errors
56 return;
57 }
58
59 // Use PlainTextFormatter (no colors needed for completion output)
60 PlainTextFormatter formatter{};
61 NullUserDiagnostics diag{&formatter};
62
63 // Get all tileset names from the project
64 ProjectTilesetMetadataProvider provider{project_path, &formatter, &diag};
65 auto result = provider.tilesets();
66
67 if (!result.has_value()) {
68 // Silent exit - no output on errors
69 return;
70 }
71
72 const auto &tileset_names = result.value();
73
74 // Output matching tileset names
75 for (const auto &name : tileset_names) {
76 // Apply prefix filter
77 if (!prefix_.empty() && !name.starts_with(prefix_)) {
78 continue;
79 }
80
81 // Apply management status filter
82 if (filter_ == "managed") {
83 if (!is_managed_tileset(project_path, name)) {
84 continue;
85 }
86 }
87 else if (filter_ == "unmanaged") {
88 if (is_managed_tileset(project_path, name)) {
89 continue;
90 }
91 }
92 // filter_ == "all" means no filtering
93
94 std::cout << name << "\n";
95 }
96 }
97
98 private:
99 static constexpr auto kCommandName = "list-tilesets";
100 static constexpr auto kCommandDesc = "List tilesets in the project.";
101 static constexpr auto kCommandGroup = "UTILITIES";
102
103 std::string project_root_;
104 std::string filter_;
105 std::string prefix_;
106
123 [[nodiscard]] static bool
124 is_managed_tileset(const std::filesystem::path &project_root, const std::string &tileset_name)
125 {
126 // Security: Validate tileset_name doesn't contain path traversal sequences.
127 // Tileset names from ProjectTilesetMetadataProvider should be safe, but validate anyway.
128 if (tileset_name.empty() || tileset_name.find('/') != std::string::npos ||
129 tileset_name.find('\\') != std::string::npos || tileset_name.find("..") != std::string::npos) {
130 return false;
131 }
132
133 // Check for porytiles manifest file using full tileset name as directory
134 const auto manifest_path = project_root / "porytiles" / "tilesets" / tileset_name / "tileset-manifest.json";
135
136 return exists(manifest_path);
137 }
138};
Command is an abstract class that provides basic command functionality for the Porytiles CLI driver.
Definition command.hpp:18
CLI::App & get_app() const
Definition command.hpp:44
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.