Open a .db file briefly, collect node/edge counts and root_path, * then append a JSON entry to arr. */
| 2499 | /* Open a .db file briefly, collect node/edge counts and root_path, |
| 2500 | * then append a JSON entry to arr. */ |
| 2501 | static void build_project_json_entry(yyjson_mut_doc *doc, yyjson_mut_val *arr, const char *dir_path, |
| 2502 | const char *name, size_t name_len, int64_t size_bytes, |
| 2503 | bool metadata_only) { |
| 2504 | (void)name_len; |
| 2505 | |
| 2506 | char full_path[CBM_SZ_2K]; |
| 2507 | snprintf(full_path, sizeof(full_path), "%s/%s", dir_path, name); |
| 2508 | |
| 2509 | /* #704: key on the db's INTERNAL project name, not its filename. Node/edge |
| 2510 | * rows are tagged with the internal name, so a drifted filename (copied or |
| 2511 | * renamed db, legacy '.'-vs-'-' username twin) would otherwise report 0 |
| 2512 | * nodes/edges and be unresolvable. Skip ghost/empty/corrupt dbs entirely so |
| 2513 | * they don't appear as resolvable projects. */ |
| 2514 | char project_name[CBM_SZ_1K]; |
| 2515 | cbm_store_t *pstore = NULL; |
| 2516 | if (!db_internal_project_name(full_path, project_name, sizeof(project_name), &pstore)) { |
| 2517 | return; /* ghost / unreadable — not a resolvable project */ |
| 2518 | } |
| 2519 | |
| 2520 | int nodes = 0; |
| 2521 | int edges = 0; |
| 2522 | if (!metadata_only) { |
| 2523 | nodes = cbm_store_count_nodes(pstore, project_name); |
| 2524 | edges = cbm_store_count_edges(pstore, project_name); |
| 2525 | } |
| 2526 | char root_path_buf[CBM_SZ_1K] = ""; |
| 2527 | cbm_project_t proj = {0}; |
| 2528 | if (cbm_store_get_project(pstore, project_name, &proj) == CBM_STORE_OK) { |
| 2529 | if (proj.root_path) { |
| 2530 | snprintf(root_path_buf, sizeof(root_path_buf), "%s", proj.root_path); |
| 2531 | } |
| 2532 | cbm_project_free_fields(&proj); |
| 2533 | } |
| 2534 | cbm_store_close(pstore); |
| 2535 | |
| 2536 | yyjson_mut_val *p = yyjson_mut_obj(doc); |
| 2537 | yyjson_mut_obj_add_strcpy(doc, p, "name", project_name); |
| 2538 | yyjson_mut_obj_add_strcpy(doc, p, "root_path", root_path_buf); |
| 2539 | /* Listing stays lean: only the branch (the one git fact that |
| 2540 | * disambiguates same-repo projects). The 12-field git block — mostly |
| 2541 | * null for non-git roots — cost ~10KB across a full cache and is one |
| 2542 | * index_status call away for the project you actually care about. */ |
| 2543 | if (!metadata_only && root_path_buf[0]) { |
| 2544 | cbm_git_context_t gctx = {0}; |
| 2545 | (void)cbm_git_context_resolve(root_path_buf, &gctx); |
| 2546 | if (gctx.is_git && gctx.branch) { |
| 2547 | yyjson_mut_obj_add_strcpy(doc, p, "branch", gctx.branch); |
| 2548 | } |
| 2549 | cbm_git_context_free(&gctx); |
| 2550 | } |
| 2551 | if (!metadata_only) { |
| 2552 | yyjson_mut_obj_add_int(doc, p, "nodes", nodes); |
| 2553 | yyjson_mut_obj_add_int(doc, p, "edges", edges); |
| 2554 | yyjson_mut_obj_add_int(doc, p, "size_bytes", size_bytes); |
| 2555 | } |
| 2556 | yyjson_mut_arr_add_val(arr, p); |
| 2557 | } |
| 2558 |
no test coverage detected