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