| 4347 | } |
| 4348 | |
| 4349 | static char *handle_check_index_coverage(cbm_mcp_server_t *srv, const char *args) { |
| 4350 | char *project = get_project_arg(args); |
| 4351 | cbm_store_t *store = resolve_store(srv, project); |
| 4352 | REQUIRE_STORE(store, project); |
| 4353 | |
| 4354 | yyjson_doc *adoc = yyjson_read(args, strlen(args), 0); |
| 4355 | yyjson_val *aroot = adoc ? yyjson_doc_get_root(adoc) : NULL; |
| 4356 | yyjson_val *paths = aroot ? yyjson_obj_get(aroot, "paths") : NULL; |
| 4357 | yyjson_val *scopes = aroot ? yyjson_obj_get(aroot, "scopes") : NULL; |
| 4358 | size_t path_count = paths && yyjson_is_arr(paths) ? yyjson_arr_size(paths) : 0U; |
| 4359 | size_t scope_count = scopes && yyjson_is_arr(scopes) ? yyjson_arr_size(scopes) : 0U; |
| 4360 | if (!aroot || (paths && !yyjson_is_arr(paths)) || (scopes && !yyjson_is_arr(scopes)) || |
| 4361 | (path_count == 0U && scope_count == 0U) || path_count > COVERAGE_PATH_MAX || |
| 4362 | scope_count > COVERAGE_SCOPE_MAX) { |
| 4363 | if (adoc) { |
| 4364 | yyjson_doc_free(adoc); |
| 4365 | } |
| 4366 | free(project); |
| 4367 | return cbm_mcp_text_result( |
| 4368 | "paths or scopes is required (arrays; max 128 paths and 32 scopes)", true); |
| 4369 | } |
| 4370 | |
| 4371 | cbm_project_t proj = {0}; |
| 4372 | bool have_project = cbm_store_get_project(store, project, &proj) == CBM_STORE_OK; |
| 4373 | cbm_coverage_meta_t meta = {0}; |
| 4374 | bool have_meta = cbm_store_coverage_meta_get(store, project, &meta) == CBM_STORE_OK; |
| 4375 | bool generation_matches = have_project && have_meta && proj.indexed_at && meta.generation && |
| 4376 | strcmp(proj.indexed_at, meta.generation) == 0; |
| 4377 | const char *recording_status = |
| 4378 | have_meta && meta.recording_status ? meta.recording_status : "unknown"; |
| 4379 | |
| 4380 | yyjson_mut_doc *doc = yyjson_mut_doc_new(NULL); |
| 4381 | yyjson_mut_val *root = yyjson_mut_obj(doc); |
| 4382 | yyjson_mut_doc_set_root(doc, root); |
| 4383 | yyjson_mut_obj_add_strcpy(doc, root, "project", project); |
| 4384 | yyjson_mut_obj_add_str(doc, root, "signal", "best_effort"); |
| 4385 | yyjson_mut_obj_add_strcpy(doc, root, "indexed_at", |
| 4386 | have_project && proj.indexed_at ? proj.indexed_at : ""); |
| 4387 | |
| 4388 | yyjson_mut_val *meta_obj = yyjson_mut_obj(doc); |
| 4389 | yyjson_mut_obj_add_strcpy(doc, meta_obj, "generation", |
| 4390 | have_meta && meta.generation ? meta.generation : ""); |
| 4391 | yyjson_mut_obj_add_strcpy(doc, meta_obj, "index_mode", |
| 4392 | have_meta && meta.index_mode ? meta.index_mode : "unknown"); |
| 4393 | yyjson_mut_obj_add_strcpy(doc, meta_obj, "recorded_at", |
| 4394 | have_meta && meta.recorded_at ? meta.recorded_at : ""); |
| 4395 | yyjson_mut_obj_add_strcpy(doc, meta_obj, "recording_status", recording_status); |
| 4396 | yyjson_mut_obj_add_int(doc, meta_obj, "ignored_files_stored", |
| 4397 | have_meta ? meta.ignored_files_stored : 0); |
| 4398 | yyjson_mut_obj_add_int(doc, meta_obj, "ignored_files_total", |
| 4399 | have_meta ? meta.ignored_files_total : 0); |
| 4400 | yyjson_mut_obj_add_bool(doc, meta_obj, "hash_records_complete", |
| 4401 | have_meta && meta.hash_records_complete); |
| 4402 | yyjson_mut_obj_add_int(doc, meta_obj, "coverage_version", |
| 4403 | have_meta ? meta.coverage_version : 0); |
| 4404 | yyjson_mut_obj_add_bool(doc, meta_obj, "generation_matches", generation_matches); |
| 4405 | yyjson_mut_obj_add_val(doc, root, "metadata", meta_obj); |
| 4406 |
no test coverage detected