yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
project_plan_commands.cc
Go to the documentation of this file.
2
3#include <fstream>
4#include <iterator>
5#include <string>
6#include <vector>
7
8#include "absl/status/status.h"
9#include "absl/strings/str_format.h"
10#include "core/project.h"
11#include "core/project_plan.h"
14#include "util/macro.h"
15
16namespace yaze::cli::handlers {
17namespace {
18
20 const char* command) {
21 const auto plan = parser.GetString("plan");
22 const auto project = parser.GetString("project");
23 const bool has_plan = plan.has_value() && !plan->empty();
24 const bool has_project = project.has_value() && !project->empty();
25 if (has_plan == has_project) {
26 return absl::InvalidArgumentError(absl::StrFormat(
27 "%s: pass exactly one of --plan or --project", command));
28 }
29 return absl::OkStatus();
30}
31
32// The plan named by --plan, or by the --project file's [planning] section.
33absl::StatusOr<std::string> ResolvePlanPath(
34 const resources::ArgumentParser& parser) {
35 if (auto plan = parser.GetString("plan");
36 plan.has_value() && !plan->empty()) {
37 return *plan;
38 }
40 RETURN_IF_ERROR(project.Open(*parser.GetString("project")));
41 const std::string path = project.PlanningFilePath();
42 if (project.planning.file.empty() || path.empty()) {
43 return absl::FailedPreconditionError(
44 absl::StrFormat("Project %s has no [planning] file", project.filepath));
45 }
46 return path;
47}
48
49absl::StatusOr<std::string> ReadText(const std::string& path) {
50 std::ifstream file(path, std::ios::binary);
51 if (!file.is_open()) {
52 return absl::NotFoundError(absl::StrFormat("Cannot read %s", path));
53 }
54 return std::string(std::istreambuf_iterator<char>(file), {});
55}
56
57template <typename Entry>
58void AddIds(resources::OutputFormatter& formatter, const char* key,
59 const std::vector<Entry>& entries) {
60 formatter.BeginArray(key);
61 for (const auto& entry : entries) {
62 formatter.AddArrayItem(entry.id);
63 }
64 formatter.EndArray();
65}
66
67} // namespace
68
70 Descriptor desc;
71 desc.display_name = "Plan Read";
72 desc.summary =
73 "Print a yaze.plan/1 file's SHA-256, entry IDs and agent status. Agents "
74 "copy sha256 into an agent update's base_sha256.";
75 desc.entries = {
76 {"--plan", "Path to the yaze.plan/1 JSON file", ""},
77 {"--project", "Path to a .yaze project; uses its [planning] file", ""},
78 };
79 return desc;
80}
81
83 const resources::ArgumentParser& parser) {
84 return ValidatePlanSource(parser, "plan-read");
85}
86
88 Rom* /*rom*/, const resources::ArgumentParser& parser,
89 resources::OutputFormatter& formatter) {
90 ASSIGN_OR_RETURN(const std::string path, ResolvePlanPath(parser));
91 std::string source;
93 core::ProjectPlan::LoadFromFile(path, &source));
94 formatter.AddField("path", path);
95 formatter.AddField("schema", std::string(core::kProjectPlanSchema));
96 formatter.AddField("sha256", core::ComputeSourceArtifactSha256(source));
97 formatter.AddField("title", plan.title);
98 AddIds(formatter, "milestones", plan.milestones);
99 AddIds(formatter, "steps", plan.route_steps);
100 AddIds(formatter, "items", plan.route_items);
101 AddIds(formatter, "decisions", plan.decisions);
102
103 const core::PlanJson* section = core::FindPlanAgentStatus(plan);
104 formatter.BeginObject("agent_status");
105 formatter.AddField("present", section != nullptr);
106 formatter.BeginArray("observations");
107 if (section != nullptr) {
108 if (auto it = section->find("observations");
109 it != section->end() && it->is_object()) {
110 for (auto obs = it->begin(); obs != it->end(); ++obs) {
111 formatter.AddArrayItem(obs.key());
112 }
113 }
114 }
115 formatter.EndArray();
116 formatter.BeginArray("orphaned");
117 for (const auto& key : core::OrphanedPlanAgentObservations(plan)) {
118 formatter.AddArrayItem(key);
119 }
120 formatter.EndArray();
121 formatter.EndObject();
122 return absl::OkStatus();
123}
124
126 const {
127 Descriptor desc;
128 desc.display_name = "Plan Agent Apply";
129 desc.summary =
130 "Check, diff and apply a yaze.plan.agent_update/1 document to a plan's "
131 "agent_status section. Human notes, answers, titles, route order and "
132 "unknown sections are never written. A stale base_sha256 is refused "
133 "before writing; an unchanged update does not write.";
134 desc.entries = {
135 {"--plan", "Path to the yaze.plan/1 JSON file", ""},
136 {"--project", "Path to a .yaze project; uses its [planning] file", ""},
137 {"--update", "Path to the agent update JSON (required)", ""},
138 {"--dry-run", "Validate and print the diff without writing", ""},
139 };
140 return desc;
141}
142
144 const resources::ArgumentParser& parser) {
145 RETURN_IF_ERROR(ValidatePlanSource(parser, "plan-agent-apply"));
146 const auto update = parser.GetString("update");
147 if (!update.has_value() || update->empty()) {
148 return absl::InvalidArgumentError("plan-agent-apply: --update is required");
149 }
150 return absl::OkStatus();
151}
152
154 Rom* /*rom*/, const resources::ArgumentParser& parser,
155 resources::OutputFormatter& formatter) {
156 ASSIGN_OR_RETURN(const std::string path, ResolvePlanPath(parser));
157 ASSIGN_OR_RETURN(const std::string text,
158 ReadText(*parser.GetString("update")));
161 const bool dry_run = parser.HasFlag("dry-run");
163 core::ApplyPlanAgentUpdateToFile(path, update, dry_run));
164 formatter.AddField("path", path);
165 formatter.AddField("dry_run", dry_run);
166 formatter.AddField("before_sha256", result.before_sha256);
167 formatter.AddField("after_sha256", result.after_sha256);
168 formatter.AddField("changed", result.diff.HasChanges());
169 formatter.AddField("written", result.written);
170 formatter.AddField("sources_changed", result.diff.sources_changed);
171 formatter.BeginArray("changes");
172 for (const auto& change : result.diff.changes) {
173 formatter.AddArrayItem(absl::StrFormat(
174 "%s %s", core::PlanAgentChangeKindName(change.kind), change.key));
175 }
176 formatter.EndArray();
177 return absl::OkStatus();
178}
179
180} // namespace yaze::cli::handlers
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
Definition rom.h:52
Descriptor Describe() const override
Provide metadata for TUI/help summaries.
absl::Status ValidateArgs(const resources::ArgumentParser &parser) override
Validate command arguments.
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
absl::Status Execute(Rom *rom, const resources::ArgumentParser &parser, resources::OutputFormatter &formatter) override
Execute the command business logic.
absl::Status ValidateArgs(const resources::ArgumentParser &parser) override
Validate command arguments.
Descriptor Describe() const override
Provide metadata for TUI/help summaries.
Utility for parsing common CLI argument patterns.
std::optional< std::string > GetString(const std::string &name) const
Parse a named argument (e.g., –format=json or –format json)
bool HasFlag(const std::string &name) const
Check if a flag is present.
Utility for consistent output formatting across commands.
void BeginArray(const std::string &key)
Begin an array.
void AddArrayItem(const std::string &item)
Add an item to current array.
void BeginObject(const std::string &title="")
Start a JSON object or text section.
void EndObject()
End a JSON object or text section.
void AddField(const std::string &key, const std::string &value)
Add a key-value pair.
std::vector< PlanMilestone > milestones
static absl::StatusOr< ProjectPlan > LoadFromFile(const std::string &path, std::string *source=nullptr)
std::vector< PlanRouteItem > route_items
std::vector< PlanRouteStep > route_steps
std::vector< PlanDecision > decisions
#define ASSIGN_OR_RETURN(type_variable_name, expression)
Definition macro.h:62
absl::Status ValidatePlanSource(const resources::ArgumentParser &parser, const char *command)
absl::StatusOr< std::string > ResolvePlanPath(const resources::ArgumentParser &parser)
void AddIds(resources::OutputFormatter &formatter, const char *key, const std::vector< Entry > &entries)
absl::StatusOr< std::string > ReadText(const std::string &path)
const PlanJson * FindPlanAgentStatus(const ProjectPlan &plan)
constexpr std::string_view kProjectPlanSchema
std::string ComputeSourceArtifactSha256(std::string_view content)
const char * PlanAgentChangeKindName(PlanAgentChangeKind kind)
nlohmann::ordered_json PlanJson
absl::StatusOr< PlanAgentFileResult > ApplyPlanAgentUpdateToFile(const std::string &path, const PlanAgentUpdate &update, bool dry_run)
std::vector< std::string > OrphanedPlanAgentObservations(const ProjectPlan &plan)
#define RETURN_IF_ERROR(expr)
Definition snes.cc:22
std::vector< PlanAgentChange > changes
static absl::StatusOr< PlanAgentUpdate > Parse(std::string_view text)
Modern project structure with comprehensive settings consolidation.
Definition project.h:290
PlanningSettings planning
Definition project.h:366
std::string PlanningFilePath() const
Definition project.cc:2760
absl::Status Open(const std::string &project_path)
Definition project.cc:607