Pharos 0.7.23
Modern Web Framework & Web App Hosting Service.
Loading...
Searching...
No Matches
native_artifact_emit.c
Go to the documentation of this file.
1/**
2 * @file
3 * @brief Builds native app and website artifact metadata and emits JSON or HTML report payloads.
4 */
5
7
8#include "native_support.h"
10
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14
15#ifdef _WIN32
16#define pharos_artifact_emit_strdup _strdup
17#else
18#define pharos_artifact_emit_strdup strdup
19#endif
20
21int count_substring_occurrences(const char *text, const char *needle) {
22 int count = 0;
23 const char *cursor = text;
24 size_t needle_len = strlen(needle);
25 if (text == NULL || needle == NULL || needle_len == 0) {
26 return 0;
27 }
28 while ((cursor = strstr(cursor, needle)) != NULL) {
29 count++;
30 cursor += needle_len;
31 }
32 return count;
33}
34
35char *json_string_dup_native(const char *value) {
36 Buffer buffer;
37 const unsigned char *cursor;
38 buffer_init(&buffer);
39 if (buffer_append(&buffer, "\"", 1) != 0) {
40 buffer_free(&buffer);
41 return NULL;
42 }
43 for (cursor = (const unsigned char *)(value != NULL ? value : ""); *cursor != '\0'; cursor++) {
44 char escaped[3];
45 if (*cursor == '\\' || *cursor == '"') {
46 escaped[0] = '\\';
47 escaped[1] = (char)*cursor;
48 escaped[2] = '\0';
49 if (buffer_append(&buffer, escaped, 2) != 0) {
50 buffer_free(&buffer);
51 return NULL;
52 }
53 } else if (*cursor == '\n') {
54 if (buffer_append(&buffer, "\\n", 2) != 0) {
55 buffer_free(&buffer);
56 return NULL;
57 }
58 } else if (*cursor == '\r') {
59 if (buffer_append(&buffer, "\\r", 2) != 0) {
60 buffer_free(&buffer);
61 return NULL;
62 }
63 } else if (*cursor == '\t') {
64 if (buffer_append(&buffer, "\\t", 2) != 0) {
65 buffer_free(&buffer);
66 return NULL;
67 }
68 } else {
69 char single = (char)*cursor;
70 if (buffer_append(&buffer, &single, 1) != 0) {
71 buffer_free(&buffer);
72 return NULL;
73 }
74 }
75 }
76 if (buffer_append(&buffer, "\"", 1) != 0) {
77 buffer_free(&buffer);
78 return NULL;
79 }
80 return buffer.data;
81}
82
83char *native_website_artifact_dir(const char *build_dir, const char *target_id) {
84 return path_join(build_dir != NULL ? build_dir : "dist", target_id);
85}
86
87char *native_website_report_path(const char *build_dir, const char *verb, const char *target_id) {
88 char *reports_dir = path_join(build_dir != NULL ? build_dir : "dist", "reports");
89 char buffer[512];
90 char *path = NULL;
91 if (reports_dir == NULL) {
92 return NULL;
93 }
94 snprintf(buffer, sizeof(buffer), "%s-website-%s.json", verb, target_id);
95 path = path_join(reports_dir, buffer);
96 free(reports_dir);
97 return path;
98}
99
100char *build_native_website_status_report_json(const char *report_id, const char *verb, const char *target_id, const char *artifact_dir) {
101 char *report_json = (char *)malloc(strlen(report_id != NULL ? report_id : "") + strlen(verb != NULL ? verb : "") + strlen(target_id != NULL ? target_id : "") + strlen(artifact_dir != NULL ? artifact_dir : "") + 256);
102 if (report_json == NULL) {
103 return NULL;
104 }
105 snprintf(report_json, strlen(report_id != NULL ? report_id : "") + strlen(verb != NULL ? verb : "") + strlen(target_id != NULL ? target_id : "") + strlen(artifact_dir != NULL ? artifact_dir : "") + 256,
106 "{\"report_id\":\"%s\",\"verb\":\"%s\",\"target_kind\":\"website\",\"target_id\":\"%s\",\"status\":\"ok\",\"artifact_dir\":\"%s\",\"runtime_launcher\":true,\"host_target\":\"native-host\"}\n",
107 report_id != NULL ? report_id : "",
108 verb != NULL ? verb : "",
109 target_id != NULL ? target_id : "",
110 artifact_dir != NULL ? artifact_dir : ""
111 );
112 return report_json;
113}
114
115char *build_native_app_index_html(const char *app_id) {
116 size_t needed = strlen(app_id != NULL ? app_id : "") + 512;
117 char *index_html = (char *)malloc(needed);
118 if (index_html == NULL) {
119 return NULL;
120 }
121 snprintf(index_html, needed, "<!doctype html>\n<html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><title>%s</title></head><body><main><h1>%s</h1><p>Pharos native app artifact built from app-owned JSON/HTML/assets.</p></main></body></html>\n", app_id != NULL ? app_id : "", app_id != NULL ? app_id : "");
122 return index_html;
123}
124
126 const char *manifest_text,
127 const char *artifact_generated_by,
128 const char *state_path,
129 const char *base_path,
130 const char *log_path,
131 const char *log_format,
132 const char *error_report_path,
133 const char *debug,
134 const char *compiled_entrypoint,
135 int route_count,
136 int surface_count,
137 int workflow_count
138) {
139 yyjson_doc *doc = NULL;
140 yyjson_mut_doc *mut_doc = NULL;
141 yyjson_mut_val *root = NULL;
142 yyjson_mut_val *runtime = NULL;
143 yyjson_mut_val *artifact = NULL;
144 yyjson_mut_val *database = NULL;
145 char *result = NULL;
146 if (manifest_text == NULL || manifest_text[0] == '\0') {
147 return NULL;
148 }
149 doc = native_json_doc_load_text(manifest_text);
150 if (doc == NULL) {
151 return NULL;
152 }
153 mut_doc = yyjson_doc_mut_copy(doc, NULL);
154 root = mut_doc != NULL ? yyjson_mut_doc_get_root(mut_doc) : NULL;
155 runtime = root != NULL ? yyjson_mut_obj_get(root, "runtime") : NULL;
156 if (root == NULL || !yyjson_mut_is_obj(root)) {
157 yyjson_mut_doc_free(mut_doc);
159 return NULL;
160 }
161 if (runtime == NULL || !yyjson_mut_is_obj(runtime)) {
162 runtime = yyjson_mut_obj(mut_doc);
163 if (runtime == NULL || !yyjson_mut_obj_put(root, yyjson_mut_strcpy(mut_doc, "runtime"), runtime)) {
164 yyjson_mut_doc_free(mut_doc);
166 return NULL;
167 }
168 }
169 yyjson_mut_obj_remove_key(runtime, "build_artifact_dir");
170 yyjson_mut_obj_remove_key(runtime, "state_path");
171 yyjson_mut_obj_remove_key(runtime, "base_path");
172 yyjson_mut_obj_remove_key(runtime, "log_path");
173 yyjson_mut_obj_remove_key(runtime, "log_format");
174 yyjson_mut_obj_remove_key(runtime, "error_report_path");
175 yyjson_mut_obj_remove_key(runtime, "debug");
176 yyjson_mut_obj_remove_key(runtime, "compiled_entrypoint");
177 if (!yyjson_mut_obj_add_strcpy(mut_doc, runtime, "build_artifact_dir", ".")
178 || !yyjson_mut_obj_add_strcpy(mut_doc, runtime, "state_path", state_path != NULL ? state_path : "")
179 || !yyjson_mut_obj_add_strcpy(mut_doc, runtime, "base_path", base_path != NULL ? base_path : "")
180 || !yyjson_mut_obj_add_strcpy(mut_doc, runtime, "log_path", log_path != NULL ? log_path : "")
181 || !yyjson_mut_obj_add_strcpy(mut_doc, runtime, "log_format", log_format != NULL ? log_format : "service")
182 || !yyjson_mut_obj_add_strcpy(mut_doc, runtime, "error_report_path", error_report_path != NULL ? error_report_path : "")
183 || !yyjson_mut_obj_add_strcpy(mut_doc, runtime, "debug", debug != NULL ? debug : "false")
184 || !yyjson_mut_obj_add_strcpy(mut_doc, runtime, "compiled_entrypoint", compiled_entrypoint != NULL ? compiled_entrypoint : "")) {
185 yyjson_mut_doc_free(mut_doc);
187 return NULL;
188 }
189
190 database = yyjson_mut_obj_get(root, "database");
191 if (database != NULL && yyjson_mut_is_obj(database)) {
192 yyjson_mut_obj_remove_key(database, "preferred_backend");
193 yyjson_mut_obj_remove_key(database, "development_backend");
194 yyjson_mut_obj_remove_key(database, "testing_backend");
195 }
196
197 artifact = yyjson_mut_obj_get(root, "artifact");
198 if (artifact == NULL || !yyjson_mut_is_obj(artifact)) {
199 artifact = yyjson_mut_obj(mut_doc);
200 if (artifact == NULL) {
201 yyjson_mut_doc_free(mut_doc);
203 return NULL;
204 }
205 yyjson_mut_obj_remove_key(root, "artifact");
206 if (!yyjson_mut_obj_put(root, yyjson_mut_strcpy(mut_doc, "artifact"), artifact)) {
207 yyjson_mut_doc_free(mut_doc);
209 return NULL;
210 }
211 } else {
212 yyjson_mut_obj_remove_key(artifact, "schema");
213 yyjson_mut_obj_remove_key(artifact, "app_root");
214 yyjson_mut_obj_remove_key(artifact, "artifact_dir");
215 yyjson_mut_obj_remove_key(artifact, "index");
216 yyjson_mut_obj_remove_key(artifact, "static_dir");
217 yyjson_mut_obj_remove_key(artifact, "media_dir");
218 yyjson_mut_obj_remove_key(artifact, "route_count");
219 yyjson_mut_obj_remove_key(artifact, "surface_count");
220 yyjson_mut_obj_remove_key(artifact, "workflow_count");
221 yyjson_mut_obj_remove_key(artifact, "generated_by");
222 }
223 if (!yyjson_mut_obj_add_strcpy(mut_doc, artifact, "schema", "pharos.app.artifact.v1")
224 || !yyjson_mut_obj_add_strcpy(mut_doc, artifact, "app_root", ".")
225 || !yyjson_mut_obj_add_strcpy(mut_doc, artifact, "artifact_dir", ".")
226 || !yyjson_mut_obj_add_strcpy(mut_doc, artifact, "index", "index.html")
227 || !yyjson_mut_obj_add_strcpy(mut_doc, artifact, "static_dir", "static")
228 || !yyjson_mut_obj_add_strcpy(mut_doc, artifact, "media_dir", "media")
229 || !yyjson_mut_obj_add_int(mut_doc, artifact, "route_count", route_count)
230 || !yyjson_mut_obj_add_int(mut_doc, artifact, "surface_count", surface_count)
231 || !yyjson_mut_obj_add_int(mut_doc, artifact, "workflow_count", workflow_count)
232 || !yyjson_mut_obj_add_strcpy(mut_doc, artifact, "generated_by", artifact_generated_by != NULL ? artifact_generated_by : "pharos-native-runtime")) {
233 yyjson_mut_doc_free(mut_doc);
235 return NULL;
236 }
237
239 yyjson_mut_doc_free(mut_doc);
241 return result;
242}
243
245 const char *artifact_dir,
246 const char *app_id,
247 const char *state_path,
248 const char *base_path,
249 const char *log_path,
250 const char *log_format,
251 const char *error_report_path,
252 const char *debug,
253 const char *host_profile,
254 const char *compiled_entrypoint
255) {
256 char *artifact_dir_json = json_string_dup_native(artifact_dir != NULL ? artifact_dir : "");
257 char *app_id_json = json_string_dup_native(app_id != NULL ? app_id : "");
258 char *state_path_json = json_string_dup_native(state_path != NULL ? state_path : "");
259 char *base_path_json = json_string_dup_native(base_path != NULL ? base_path : "");
260 char *log_path_json = json_string_dup_native(log_path != NULL ? log_path : "");
261 char *log_format_json = json_string_dup_native(log_format != NULL ? log_format : "service");
262 char *error_report_path_json = json_string_dup_native(error_report_path != NULL ? error_report_path : "");
263 char *debug_json = json_string_dup_native(debug != NULL ? debug : "false");
264 char *host_profile_json = json_string_dup_native(host_profile != NULL ? host_profile : "dynamic-app");
265 char *compiled_entrypoint_json = json_string_dup_native(compiled_entrypoint != NULL ? compiled_entrypoint : "");
266 char *report_json;
267 if (artifact_dir_json == NULL || app_id_json == NULL || state_path_json == NULL || base_path_json == NULL || log_path_json == NULL || log_format_json == NULL || error_report_path_json == NULL || debug_json == NULL || host_profile_json == NULL || compiled_entrypoint_json == NULL) {
268 free(artifact_dir_json);
269 free(app_id_json);
270 free(state_path_json);
271 free(base_path_json);
272 free(log_path_json);
273 free(log_format_json);
274 free(error_report_path_json);
275 free(debug_json);
276 free(host_profile_json);
277 free(compiled_entrypoint_json);
278 return NULL;
279 }
280 report_json = (char *)malloc(strlen(artifact_dir_json) + strlen(app_id_json) + strlen(state_path_json) + strlen(base_path_json) + strlen(log_path_json) + strlen(log_format_json) + strlen(error_report_path_json) + strlen(debug_json) + strlen(host_profile_json) + strlen(compiled_entrypoint_json) + 512);
281 if (report_json != NULL) {
282 snprintf(report_json, strlen(artifact_dir_json) + strlen(app_id_json) + strlen(state_path_json) + strlen(base_path_json) + strlen(log_path_json) + strlen(log_format_json) + strlen(error_report_path_json) + strlen(debug_json) + strlen(host_profile_json) + strlen(compiled_entrypoint_json) + 512,
283 "{\"report_id\":\"pharos-local-app-build-v1\",\"target_kind\":\"app\",\"target_id\":%s,\"status\":\"built\",\"artifact_dir\":%s,\"state_path\":%s,\"base_path\":%s,\"log_path\":%s,\"log_format\":%s,\"error_report_path\":%s,\"debug\":%s,\"host_profile\":%s,\"compiled_entrypoint\":%s,\"generated_by\":\"pharos-native-runtime\"}\n",
284 app_id_json, artifact_dir_json, state_path_json, base_path_json, log_path_json, log_format_json, error_report_path_json, debug_json, host_profile_json, compiled_entrypoint_json);
285 }
286 free(artifact_dir_json);
287 free(app_id_json);
288 free(state_path_json);
289 free(base_path_json);
290 free(log_path_json);
291 free(log_format_json);
292 free(error_report_path_json);
293 free(debug_json);
294 free(host_profile_json);
295 free(compiled_entrypoint_json);
296 return report_json;
297}
char * build_native_app_index_html(const char *app_id)
Builds the default index.html payload for a native app artifact.
char * native_website_artifact_dir(const char *build_dir, const char *target_id)
Builds the artifact directory path for a generated website target.
char * json_string_dup_native(const char *value)
Duplicates a string while escaping it for JSON string output.
char * native_website_report_path(const char *build_dir, const char *verb, const char *target_id)
Builds the report file path for a website build or status operation.
int count_substring_occurrences(const char *text, const char *needle)
Counts how many times a substring occurs in a string.
char * build_native_app_build_report_json(const char *artifact_dir, const char *app_id, const char *state_path, const char *base_path, const char *log_path, const char *log_format, const char *error_report_path, const char *debug, const char *host_profile, const char *compiled_entrypoint)
Builds a JSON report describing a native app build result.
char * build_artifact_local_app_manifest_json(const char *manifest_text, const char *artifact_generated_by, const char *state_path, const char *base_path, const char *log_path, const char *log_format, const char *error_report_path, const char *debug, const char *compiled_entrypoint, int route_count, int surface_count, int workflow_count)
Builds an artifact-localized copy of the app-owned manifest.
char * build_native_website_status_report_json(const char *report_id, const char *verb, const char *target_id, const char *artifact_dir)
Builds a JSON status report for a native website artifact.
Declares helpers that build native artifact and report payload strings.
void buffer_init(Buffer *buffer)
Initializes a growable buffer to an empty state.
char * path_join(const char *left, const char *right)
Joins two path segments using the native path separator.
void buffer_free(Buffer *buffer)
Releases memory owned by a growable buffer.
int buffer_append(Buffer *buffer, const void *data, size_t len)
Appends bytes to a growable buffer and maintains a trailing NUL byte.
Declares shared low-level support helpers used across the Pharos native runtime.
void native_json_doc_free(yyjson_doc *doc)
Releases a document returned by the native yyjson helpers.
yyjson_doc * native_json_doc_load_text(const char *text)
Parses JSON text into an owned yyjson document.
Declares shared helper wrappers around vendored yyjson parsing and serialization APIs.
Growable byte buffer used by parsing and string assembly helpers.
char * data
void yyjson_mut_doc_free(yyjson_mut_doc *doc)
Definition yyjson.c:2653
yyjson_mut_doc * yyjson_doc_mut_copy(const yyjson_doc *doc, const yyjson_alc *alc)
Definition yyjson.c:2678
yyjson_api_inline yyjson_mut_val * yyjson_mut_obj_remove_key(yyjson_mut_val *obj, const char *key)
Definition yyjson.h:7316
yyjson_api_inline yyjson_mut_val * yyjson_mut_obj_get(const yyjson_mut_val *obj, const char *key)
Definition yyjson.h:6970
yyjson_api_inline bool yyjson_mut_obj_add_int(yyjson_mut_doc *doc, yyjson_mut_val *obj, const char *key, int64_t val)
Definition yyjson.h:7425
yyjson_api_inline yyjson_mut_val * yyjson_mut_obj(yyjson_mut_doc *doc)
Definition yyjson.h:7086
yyjson_api_inline char * yyjson_mut_write(const yyjson_mut_doc *doc, yyjson_write_flag flg, size_t *len)
Definition yyjson.h:1604
yyjson_api_inline yyjson_mut_val * yyjson_mut_strcpy(yyjson_mut_doc *doc, const char *str)
Definition yyjson.h:6282
yyjson_api_inline bool yyjson_mut_is_obj(const yyjson_mut_val *val)
Definition yyjson.h:5978
yyjson_api_inline yyjson_mut_val * yyjson_mut_doc_get_root(yyjson_mut_doc *doc)
Definition yyjson.h:5915
yyjson_api_inline bool yyjson_mut_obj_add_strcpy(yyjson_mut_doc *doc, yyjson_mut_val *obj, const char *key, const char *val)
Definition yyjson.h:7479
static const yyjson_write_flag YYJSON_WRITE_ESCAPE_UNICODE
Definition yyjson.h:1251
yyjson_api_inline bool yyjson_mut_obj_put(yyjson_mut_val *obj, yyjson_mut_val *key, yyjson_mut_val *val)
Definition yyjson.h:7259
static const yyjson_write_flag YYJSON_WRITE_PRETTY
Definition yyjson.h:1248