chore: auto-commit (4 archivos)
- data.h - data_http.cpp - views.cpp - appicon.ico Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
BIN
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
@@ -46,6 +46,10 @@ struct FunctionRow {
|
|||||||
std::string description;
|
std::string description;
|
||||||
std::string created_at;
|
std::string created_at;
|
||||||
bool tested = false;
|
bool tested = false;
|
||||||
|
// JSON array string ("[\"a\",\"b\"]") con IDs de funciones consumidas.
|
||||||
|
// Se carga junto con la lista para soportar reverse lookup "Used by" en
|
||||||
|
// el tab Dependencies del Explorer sin endpoint extra.
|
||||||
|
std::string uses_functions;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AppRow {
|
struct AppRow {
|
||||||
|
|||||||
+2
-1
@@ -373,7 +373,7 @@ bool load_all_functions_http(const std::string& api_url,
|
|||||||
HttpClient cli(host, port);
|
HttpClient cli(host, port);
|
||||||
auto j = api_query(cli,
|
auto j = api_query(cli,
|
||||||
"SELECT id, name, lang, domain, kind, purity, description, "
|
"SELECT id, name, lang, domain, kind, purity, description, "
|
||||||
"created_at, tested FROM functions ORDER BY name");
|
"created_at, tested, uses_functions FROM functions ORDER BY name");
|
||||||
if (j.is_null() || !j.contains("rows")) return false;
|
if (j.is_null() || !j.contains("rows")) return false;
|
||||||
|
|
||||||
out.clear();
|
out.clear();
|
||||||
@@ -389,6 +389,7 @@ bool load_all_functions_http(const std::string& api_url,
|
|||||||
r.description = extract_str(row, 6);
|
r.description = extract_str(row, 6);
|
||||||
r.created_at = extract_str(row, 7);
|
r.created_at = extract_str(row, 7);
|
||||||
r.tested = extract_row_int(row, 8) != 0;
|
r.tested = extract_row_int(row, 8) != 0;
|
||||||
|
r.uses_functions = extract_str(row, 9);
|
||||||
out.push_back(std::move(r));
|
out.push_back(std::move(r));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -325,6 +325,31 @@ static int g_explorer_test_idx = 0;
|
|||||||
static char g_explorer_filter[128] = {};
|
static char g_explorer_filter[128] = {};
|
||||||
static int g_explorer_lang_idx = 0;
|
static int g_explorer_lang_idx = 0;
|
||||||
static int g_explorer_domain_idx = 0;
|
static int g_explorer_domain_idx = 0;
|
||||||
|
// Caches del tab Dependencies — se recomputan en explorer_select() para no
|
||||||
|
// reparsar JSON cada frame. uses_funcs/uses_types: parse de d.uses_functions
|
||||||
|
// y d.uses_types. used_by: reverse lookup cliente-side sobre g_explorer_funcs.
|
||||||
|
static std::vector<std::string> g_explorer_uses_funcs;
|
||||||
|
static std::vector<std::string> g_explorer_uses_types;
|
||||||
|
static std::vector<std::string> g_explorer_used_by;
|
||||||
|
|
||||||
|
// Parser tolerante de arrays JSON de strings (`["a","b"]`). Las entradas de
|
||||||
|
// la BD son IDs `[a-z0-9_]+` sin escapes, asi que un walker simple basta y
|
||||||
|
// evita anadir dependencia a nlohmann/json (que vive solo en data_http.cpp).
|
||||||
|
static std::vector<std::string> parse_string_array_json(const std::string& json_str) {
|
||||||
|
std::vector<std::string> out;
|
||||||
|
if (json_str.empty() || json_str == "[]") return out;
|
||||||
|
size_t i = 0, n = json_str.size();
|
||||||
|
while (i < n) {
|
||||||
|
while (i < n && json_str[i] != '"') i++;
|
||||||
|
if (i >= n) break;
|
||||||
|
i++; // past opening "
|
||||||
|
size_t start = i;
|
||||||
|
while (i < n && json_str[i] != '"') i++;
|
||||||
|
if (i > start) out.emplace_back(json_str.substr(start, i - start));
|
||||||
|
if (i < n) i++; // past closing "
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
static void trigger_reload() {
|
static void trigger_reload() {
|
||||||
ImGui::GetIO().UserData = reinterpret_cast<void*>(1);
|
ImGui::GetIO().UserData = reinterpret_cast<void*>(1);
|
||||||
@@ -1378,9 +1403,22 @@ static void explorer_select(const std::string& id) {
|
|||||||
g_explorer_detail = FunctionDetail{};
|
g_explorer_detail = FunctionDetail{};
|
||||||
g_explorer_tests.clear();
|
g_explorer_tests.clear();
|
||||||
g_explorer_test_idx = 0;
|
g_explorer_test_idx = 0;
|
||||||
|
g_explorer_uses_funcs.clear();
|
||||||
|
g_explorer_uses_types.clear();
|
||||||
|
g_explorer_used_by.clear();
|
||||||
if (!g_api_url.empty() && !id.empty()) {
|
if (!g_api_url.empty() && !id.empty()) {
|
||||||
load_function_detail_http(g_api_url, id, g_explorer_detail);
|
load_function_detail_http(g_api_url, id, g_explorer_detail);
|
||||||
load_unit_tests_http(g_api_url, id, g_explorer_tests);
|
load_unit_tests_http(g_api_url, id, g_explorer_tests);
|
||||||
|
// Caches del tab Dependencies — forward + reverse.
|
||||||
|
g_explorer_uses_funcs = parse_string_array_json(g_explorer_detail.uses_functions);
|
||||||
|
g_explorer_uses_types = parse_string_array_json(g_explorer_detail.uses_types);
|
||||||
|
for (const auto& f : g_explorer_funcs) {
|
||||||
|
if (f.id == id) continue;
|
||||||
|
auto deps = parse_string_array_json(f.uses_functions);
|
||||||
|
for (const auto& dep : deps) {
|
||||||
|
if (dep == id) { g_explorer_used_by.push_back(f.id); break; }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1574,6 +1612,97 @@ void draw_functions_explorer() {
|
|||||||
}
|
}
|
||||||
ImGui::EndTabItem();
|
ImGui::EndTabItem();
|
||||||
}
|
}
|
||||||
|
// Tab Dependencies: muestra uses_functions, uses_types, y reverse
|
||||||
|
// lookup "used by" (clientes-side sobre g_explorer_funcs). Caches
|
||||||
|
// (g_explorer_uses_*) se rellenan en explorer_select().
|
||||||
|
int total_deps = (int)(g_explorer_uses_funcs.size()
|
||||||
|
+ g_explorer_uses_types.size());
|
||||||
|
char deps_label[32];
|
||||||
|
std::snprintf(deps_label, sizeof(deps_label),
|
||||||
|
"Dependencies (%d)", total_deps);
|
||||||
|
if (ImGui::BeginTabItem(deps_label)) {
|
||||||
|
ImGui::BeginChild("##deps_scroll", ImVec2(0, 0));
|
||||||
|
|
||||||
|
// Section 1: Uses functions (clickables; color dim si la id
|
||||||
|
// no resuelve en el catalogo actual).
|
||||||
|
char hdr1[64];
|
||||||
|
std::snprintf(hdr1, sizeof(hdr1),
|
||||||
|
"Uses functions (%zu)",
|
||||||
|
g_explorer_uses_funcs.size());
|
||||||
|
if (ImGui::CollapsingHeader(hdr1,
|
||||||
|
ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||||
|
if (g_explorer_uses_funcs.empty()) {
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text,
|
||||||
|
fn_tokens::colors::text_dim);
|
||||||
|
ImGui::TextUnformatted("(none)");
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
} else {
|
||||||
|
for (const auto& fid : g_explorer_uses_funcs) {
|
||||||
|
bool resolves = false;
|
||||||
|
for (const auto& f : g_explorer_funcs) {
|
||||||
|
if (f.id == fid) { resolves = true; break; }
|
||||||
|
}
|
||||||
|
ImVec4 col = resolves
|
||||||
|
? fn_tokens::colors::text
|
||||||
|
: fn_tokens::colors::text_dim;
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, col);
|
||||||
|
ImGui::PushID(fid.c_str());
|
||||||
|
if (ImGui::Selectable(fid.c_str())) {
|
||||||
|
if (resolves) explorer_select(fid);
|
||||||
|
}
|
||||||
|
ImGui::PopID();
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Section 2: Uses types — sin navegacion (no hay type explorer
|
||||||
|
// todavia); bullets para indicar listado pasivo.
|
||||||
|
char hdr2[64];
|
||||||
|
std::snprintf(hdr2, sizeof(hdr2),
|
||||||
|
"Uses types (%zu)",
|
||||||
|
g_explorer_uses_types.size());
|
||||||
|
if (ImGui::CollapsingHeader(hdr2,
|
||||||
|
ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||||
|
if (g_explorer_uses_types.empty()) {
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text,
|
||||||
|
fn_tokens::colors::text_dim);
|
||||||
|
ImGui::TextUnformatted("(none)");
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
} else {
|
||||||
|
for (const auto& tid : g_explorer_uses_types) {
|
||||||
|
ImGui::BulletText("%s", tid.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Section 3: Used by (reverse) — clientes detectados al
|
||||||
|
// seleccionar la funcion.
|
||||||
|
char hdr3[64];
|
||||||
|
std::snprintf(hdr3, sizeof(hdr3),
|
||||||
|
"Used by (%zu)",
|
||||||
|
g_explorer_used_by.size());
|
||||||
|
if (ImGui::CollapsingHeader(hdr3,
|
||||||
|
ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||||
|
if (g_explorer_used_by.empty()) {
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text,
|
||||||
|
fn_tokens::colors::text_dim);
|
||||||
|
ImGui::TextUnformatted("(no consumers in catalog)");
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
} else {
|
||||||
|
for (const auto& fid : g_explorer_used_by) {
|
||||||
|
ImGui::PushID(fid.c_str());
|
||||||
|
if (ImGui::Selectable(fid.c_str())) {
|
||||||
|
explorer_select(fid);
|
||||||
|
}
|
||||||
|
ImGui::PopID();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndChild();
|
||||||
|
ImGui::EndTabItem();
|
||||||
|
}
|
||||||
if (ImGui::BeginTabItem("Metadata")) {
|
if (ImGui::BeginTabItem("Metadata")) {
|
||||||
ImGui::BeginChild("##meta_scroll", ImVec2(0, 0));
|
ImGui::BeginChild("##meta_scroll", ImVec2(0, 0));
|
||||||
auto kv = [](const char* k, const std::string& v) {
|
auto kv = [](const char* k, const std::string& v) {
|
||||||
@@ -1590,8 +1719,7 @@ void draw_functions_explorer() {
|
|||||||
kv("Created:", d.created_at);
|
kv("Created:", d.created_at);
|
||||||
kv("Returns:", d.returns);
|
kv("Returns:", d.returns);
|
||||||
kv("Error type:", d.error_type);
|
kv("Error type:", d.error_type);
|
||||||
kv("Uses functions:", d.uses_functions);
|
// Uses functions/types se muestran en el tab Dependencies.
|
||||||
kv("Uses types:", d.uses_types);
|
|
||||||
kv("Params schema:", d.params_schema);
|
kv("Params schema:", d.params_schema);
|
||||||
kv("Example:", d.example);
|
kv("Example:", d.example);
|
||||||
kv("Notes:", d.notes);
|
kv("Notes:", d.notes);
|
||||||
|
|||||||
Reference in New Issue
Block a user