mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-04 10:48:20 +00:00
Diagrams name the parts of a system and how they connect; an OpenAPI document names its endpoints and their handlers. Both now enter the graph. Diagrams: - a Diagram node per diagram: a fenced block of a Markdown file (under its Section), a directive of a reST file (`.. mermaid::`, `.. uml::`, `.. graphviz::`, Sphinx `.. digraph::`), an Asciidoctor Diagram block of an AsciiDoc file (`[plantuml]`, `[source,mermaid]`, ...), or a diagram file of its own, named by its title, else by its file; - a DiagramElement node per element, DEFINES from its Diagram, and a DIAGRAM_ARROW edge per arrow (label, style, line); - formats: Mermaid (flowchart, sequence, class, ER, state, C4, architecture), PlantUML, Graphviz DOT, D2, draw.io (plain, compressed, .drawio.svg) and Excalidraw (.excalidraw, .excalidraw.json, .excalidraw.svg); .drawio.svg and .excalidraw.svg are no longer dropped as images; - an element's explicit link (Mermaid click/link, PlantUML [[url]], url of and !include, draw.io and Excalidraw link, DOT URL/href, D2 link) is a doc link read as a Markdown link, MENTIONS when it resolves. OpenAPI: - each operation of an OpenAPI or Swagger document (openapi.json and swagger.json, now read with the JSON grammar but no node per key, and any YAML or JSON file whose root says openapi/swagger) is a Route, named by the same QN a route its handler registers in code gets (cbm_route_canon_path, moved into the extraction library); - its operationId is a doc link to the handler: a dotted one (Connexion, with x-openapi-router-controller) as a qualified code name, a bare one when exactly one function or method of the repository has that name. The eight new link families are held until their held-out audit passes. Read on real repositories, which shaped the rules: Excalidraw drawings put text over shapes and draw arrows up to them without binding, so loose text names the smallest box it sits in and a loose arrow end meets the smallest named box it touches (the drawing's frame only at its border); arrows bound to free text meet the text; D2 block strings and nested blocks; DOT line breaks and sentence-long ids; PlantUML kinds of classes inside packages and nested classes (`..+`, nested -> container, so they do not collide with the container's association). Connexion's 24 specs: 160 operationIds, all linked to their handlers. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
99 lines
3.3 KiB
C
99 lines
3.3 KiB
C
/*
|
|
* route_path.c -- one Route QN per endpoint, whoever names it: a client call
|
|
* site, a handler's decorator or an OpenAPI operation (moved here from the
|
|
* route pass so extraction can name a Route as the pipeline does).
|
|
*/
|
|
#include "cbm.h"
|
|
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
|
|
/* True for characters that may appear in a ":name" route parameter. */
|
|
static inline bool is_route_ident_char(char c) {
|
|
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_';
|
|
}
|
|
|
|
/* Canonicalize route-path parameter placeholders to a single "{}" token so that
|
|
* client call sites and server handlers rendezvous on the same Route QN
|
|
* regardless of framework syntax. Each parameter token collapses to "{}":
|
|
*
|
|
* :name Express / React-Router / Rails / typical JS API clients
|
|
* {name} Axum / Spring / OpenAPI / ASP.NET
|
|
* <name> Flask / Rocket (incl. typed "<int:id>")
|
|
* ${...} JS template interpolation captured into the path
|
|
*
|
|
* Parameter names are intentionally discarded so the same logical endpoint
|
|
* matches across services that name the path variable differently. Static path
|
|
* text is copied verbatim; the result never exceeds the input length. */
|
|
const char *cbm_route_canon_path(const char *in, char *out, size_t out_sz) {
|
|
if (out == NULL || out_sz == 0) {
|
|
/* WHY: cppcheck value-flow follows a caller that passes an as-yet
|
|
* uninitialized output buffer (e.g. `char cpath[256];`) and flags this
|
|
* `return out` as uninitvar. False positive: we only return the pointer
|
|
* here and never read the buffer; returning it (NULL, or a buffer too
|
|
* small to write into) is the intended early-out contract. */
|
|
// cppcheck-suppress uninitvar
|
|
return out;
|
|
}
|
|
if (in == NULL) {
|
|
out[0] = '\0';
|
|
return out;
|
|
}
|
|
const size_t last = out_sz - 1;
|
|
size_t oi = 0;
|
|
size_t i = 0;
|
|
while (in[i] != '\0' && oi < last) {
|
|
char c = in[i];
|
|
bool at_seg_start = (oi == 0) || (out[oi - 1] == '/');
|
|
bool is_param = false;
|
|
|
|
if (c == ':' && at_seg_start && is_route_ident_char(in[i + 1])) {
|
|
i++;
|
|
while (in[i] != '\0' && is_route_ident_char(in[i])) {
|
|
i++;
|
|
}
|
|
is_param = true;
|
|
} else if (c == '{') {
|
|
i++;
|
|
while (in[i] != '\0' && in[i] != '}' && in[i] != '/') {
|
|
i++;
|
|
}
|
|
if (in[i] == '}') {
|
|
i++;
|
|
}
|
|
is_param = true;
|
|
} else if (c == '<') {
|
|
i++;
|
|
while (in[i] != '\0' && in[i] != '>' && in[i] != '/') {
|
|
i++;
|
|
}
|
|
if (in[i] == '>') {
|
|
i++;
|
|
}
|
|
is_param = true;
|
|
} else if (c == '$' && in[i + 1] == '{') {
|
|
i += 2;
|
|
while (in[i] != '\0' && in[i] != '}' && in[i] != '/') {
|
|
i++;
|
|
}
|
|
if (in[i] == '}') {
|
|
i++;
|
|
}
|
|
is_param = true;
|
|
}
|
|
|
|
if (is_param) {
|
|
if (oi + 2 > last) {
|
|
break;
|
|
}
|
|
out[oi++] = '{';
|
|
out[oi++] = '}';
|
|
continue;
|
|
}
|
|
out[oi++] = c;
|
|
i++;
|
|
}
|
|
out[oi] = '\0';
|
|
return out;
|
|
}
|