Count only edges of `edge_type` between the exact named source/target nodes. * The parity and incremental guards deliberately use unique symbol names, so * this canonicalizes across independent stores without depending on node IDs * or project-prefixed qualified names. */
| 1094 | * this canonicalizes across independent stores without depending on node IDs |
| 1095 | * or project-prefixed qualified names. */ |
| 1096 | static int named_edge_count(cbm_store_t *s, const char *project, const char *edge_type, |
| 1097 | const char *source_name, const char *target_name) { |
| 1098 | cbm_edge_t *edges = NULL; |
| 1099 | int edge_count = 0; |
| 1100 | if (cbm_store_find_edges_by_type(s, project, edge_type, &edges, &edge_count) != CBM_STORE_OK) { |
| 1101 | return -1; |
| 1102 | } |
| 1103 | |
| 1104 | int matches = 0; |
| 1105 | for (int i = 0; i < edge_count; i++) { |
| 1106 | cbm_node_t source = {0}; |
| 1107 | cbm_node_t target = {0}; |
| 1108 | int source_ok = cbm_store_find_node_by_id(s, edges[i].source_id, &source) == CBM_STORE_OK; |
| 1109 | int target_ok = cbm_store_find_node_by_id(s, edges[i].target_id, &target) == CBM_STORE_OK; |
| 1110 | if (source_ok && target_ok && source.name && target.name && |
| 1111 | strcmp(source.name, source_name) == 0 && strcmp(target.name, target_name) == 0) { |
| 1112 | matches++; |
| 1113 | } |
| 1114 | cbm_node_free_fields(&source); |
| 1115 | cbm_node_free_fields(&target); |
| 1116 | } |
| 1117 | if (edges) { |
| 1118 | cbm_store_free_edges(edges, edge_count); |
| 1119 | } |
| 1120 | return matches; |
| 1121 | } |
| 1122 | |
| 1123 | /* Like named_edge_count, but distinguish same-named targets by their source |
| 1124 | * file. Semantic-control fixtures intentionally keep the exported short name |
no test coverage detected