| 94 | /* ── Save ────────────────────────────────────────────────────── */ |
| 95 | |
| 96 | void cbm_ui_config_save(const cbm_ui_config_t *cfg) { |
| 97 | char path[CBM_SZ_1K]; |
| 98 | cbm_ui_config_path(path, (int)sizeof(path)); |
| 99 | |
| 100 | /* Ensure directory exists (recursive) */ |
| 101 | char dir[CBM_SZ_1K]; |
| 102 | snprintf(dir, sizeof(dir), "%s", path); |
| 103 | char *slash = strrchr(dir, '/'); |
| 104 | if (slash) { |
| 105 | *slash = '\0'; |
| 106 | cbm_mkdir_p(dir, 0750); |
| 107 | } |
| 108 | |
| 109 | yyjson_mut_doc *doc = yyjson_mut_doc_new(NULL); |
| 110 | yyjson_mut_val *root = yyjson_mut_obj(doc); |
| 111 | yyjson_mut_doc_set_root(doc, root); |
| 112 | |
| 113 | yyjson_mut_obj_add_bool(doc, root, "ui_enabled", cfg->ui_enabled); |
| 114 | yyjson_mut_obj_add_int(doc, root, "ui_port", cfg->ui_port); |
| 115 | |
| 116 | size_t json_len = 0; |
| 117 | char *json = yyjson_mut_write(doc, YYJSON_WRITE_PRETTY, &json_len); |
| 118 | yyjson_mut_doc_free(doc); |
| 119 | |
| 120 | if (!json) { |
| 121 | cbm_log_error("ui.config.write_fail", "reason", "serialize"); |
| 122 | return; |
| 123 | } |
| 124 | |
| 125 | FILE *f = fopen(path, "wb"); |
| 126 | if (!f) { |
| 127 | cbm_log_error("ui.config.write_fail", "path", path); |
| 128 | free(json); |
| 129 | return; |
| 130 | } |
| 131 | |
| 132 | fwrite(json, SKIP_ONE, json_len, f); |
| 133 | fclose(f); |
| 134 | free(json); |
| 135 | |
| 136 | cbm_log_debug("ui.config.saved", "path", path); |
| 137 | } |
no test coverage detected