#include "data.h" #include #include #include #include #ifdef _WIN32 #include #include #endif // Helper: execute a query and call row_fn for each row template static bool query(sqlite3* db, const char* sql, F row_fn) { sqlite3_stmt* stmt = nullptr; if (sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr) != SQLITE_OK) { fprintf(stderr, "SQL error: %s\n query: %s\n", sqlite3_errmsg(db), sql); return false; } while (sqlite3_step(stmt) == SQLITE_ROW) { row_fn(stmt); } sqlite3_finalize(stmt); return true; } static std::string col_str(sqlite3_stmt* s, int i) { const char* t = reinterpret_cast(sqlite3_column_text(s, i)); return t ? t : ""; } static int col_int(sqlite3_stmt* s, int i) { return sqlite3_column_int(s, i); } void RegistryData::prepare_chart_data() { lang_labels.clear(); lang_values.clear(); for (auto& lc : by_lang) { lang_labels.push_back(lc.lang); lang_values.push_back(static_cast(lc.count)); } domain_labels.clear(); domain_values.clear(); for (auto& dc : by_domain) { domain_labels.push_back(dc.domain); domain_values.push_back(static_cast(dc.count)); } kind_labels.clear(); kind_values.clear(); for (auto& kc : by_kind) { kind_labels.push_back(kc.kind); kind_values.push_back(static_cast(kc.count)); } date_labels.clear(); date_values.clear(); for (auto& dc : by_date) { date_labels.push_back(dc.date); date_values.push_back(static_cast(dc.count)); } } // Copy file src to dst. Returns true on success. static bool copy_file(const char* src, const char* dst) { #ifdef _WIN32 return CopyFileA(src, dst, FALSE) != 0; #else FILE* in = fopen(src, "rb"); if (!in) return false; FILE* out = fopen(dst, "wb"); if (!out) { fclose(in); return false; } char buf[65536]; size_t n; while ((n = fread(buf, 1, sizeof(buf), in)) > 0) fwrite(buf, 1, n, out); fclose(in); fclose(out); return true; #endif } // On Windows, if the path is a network/UNC path (like \\wsl.localhost\...), // copy the DB to a local temp file so SQLite locking works correctly. // Returns the path to use (may be the original or a temp copy). static std::string ensure_local_db(const char* db_path) { #ifdef _WIN32 // Detect UNC / network paths if (strncmp(db_path, "\\\\", 2) == 0 || strncmp(db_path, "//", 2) == 0) { char temp_dir[MAX_PATH] = {0}; GetTempPathA(MAX_PATH, temp_dir); std::string local_path = std::string(temp_dir) + "fn_registry_dashboard.db"; fprintf(stdout, "Copying DB to local temp: %s\n", local_path.c_str()); if (copy_file(db_path, local_path.c_str())) { return local_path; } fprintf(stderr, "Warning: failed to copy DB locally, using original path\n"); } #endif return db_path; } bool load_registry_data(const char* db_path, RegistryData& out) { std::string effective_path = ensure_local_db(db_path); sqlite3* db = nullptr; if (sqlite3_open_v2(effective_path.c_str(), &db, SQLITE_OPEN_READONLY, nullptr) != SQLITE_OK) { fprintf(stderr, "Cannot open %s: %s\n", effective_path.c_str(), sqlite3_errmsg(db)); return false; } sqlite3_busy_timeout(db, 3000); // --- Counts --- query(db, "SELECT COUNT(*) FROM functions", [&](sqlite3_stmt* s) { out.stats.total_functions = col_int(s, 0); }); query(db, "SELECT COUNT(*) FROM types", [&](sqlite3_stmt* s) { out.stats.total_types = col_int(s, 0); }); query(db, "SELECT COUNT(*) FROM apps", [&](sqlite3_stmt* s) { out.stats.total_apps = col_int(s, 0); }); query(db, "SELECT COUNT(*) FROM analysis", [&](sqlite3_stmt* s) { out.stats.total_analysis = col_int(s, 0); }); query(db, "SELECT COUNT(*) FROM unit_tests", [&](sqlite3_stmt* s) { out.stats.total_unit_tests = col_int(s, 0); }); query(db, "SELECT COUNT(*) FROM proposals", [&](sqlite3_stmt* s) { out.stats.total_proposals = col_int(s, 0); }); query(db, "SELECT COUNT(*) FROM functions WHERE tested = 1", [&](sqlite3_stmt* s) { out.stats.tested_functions = col_int(s, 0); }); query(db, "SELECT COUNT(*) FROM functions WHERE purity = 'pure'", [&](sqlite3_stmt* s) { out.stats.pure_functions = col_int(s, 0); }); query(db, "SELECT COUNT(*) FROM functions WHERE purity = 'impure'", [&](sqlite3_stmt* s) { out.stats.impure_functions = col_int(s, 0); }); // --- By language --- out.by_lang.clear(); query(db, "SELECT lang, COUNT(*) as cnt FROM functions GROUP BY lang ORDER BY cnt DESC", [&](sqlite3_stmt* s) { out.by_lang.push_back({col_str(s, 0), col_int(s, 1)}); }); // --- By domain --- out.by_domain.clear(); query(db, "SELECT domain, COUNT(*) as cnt FROM functions GROUP BY domain ORDER BY cnt DESC", [&](sqlite3_stmt* s) { out.by_domain.push_back({col_str(s, 0), col_int(s, 1)}); }); // --- By kind --- out.by_kind.clear(); query(db, "SELECT kind, COUNT(*) as cnt FROM functions GROUP BY kind ORDER BY cnt DESC", [&](sqlite3_stmt* s) { out.by_kind.push_back({col_str(s, 0), col_int(s, 1)}); }); // --- By date (last 30 days) --- out.by_date.clear(); query(db, "SELECT date(created_at) as d, COUNT(*) as cnt FROM functions " "WHERE created_at >= date('now', '-30 days') " "GROUP BY d ORDER BY d", [&](sqlite3_stmt* s) { out.by_date.push_back({col_str(s, 0), col_int(s, 1)}); }); // --- Recent functions (last 20) --- out.recent_funcs.clear(); query(db, "SELECT id, name, lang, domain, kind, purity, description, created_at, tested " "FROM functions ORDER BY created_at DESC LIMIT 20", [&](sqlite3_stmt* s) { FunctionRow r; r.id = col_str(s, 0); r.name = col_str(s, 1); r.lang = col_str(s, 2); r.domain = col_str(s, 3); r.kind = col_str(s, 4); r.purity = col_str(s, 5); r.description = col_str(s, 6); r.created_at = col_str(s, 7); r.tested = col_int(s, 8) != 0; out.recent_funcs.push_back(std::move(r)); }); // --- Apps --- out.apps.clear(); query(db, "SELECT id, name, lang, domain, description, framework, repo_url, dir_path FROM apps ORDER BY name", [&](sqlite3_stmt* s) { AppRow r; r.id = col_str(s, 0); r.name = col_str(s, 1); r.lang = col_str(s, 2); r.domain = col_str(s, 3); r.description = col_str(s, 4); r.framework = col_str(s, 5); r.repo_url = col_str(s, 6); r.dir_path = col_str(s, 7); out.apps.push_back(std::move(r)); }); // --- Analysis --- out.analyses.clear(); query(db, "SELECT id, name, lang, domain, description FROM analysis ORDER BY name", [&](sqlite3_stmt* s) { AnalysisRow r; r.id = col_str(s, 0); r.name = col_str(s, 1); r.lang = col_str(s, 2); r.domain = col_str(s, 3); r.description = col_str(s, 4); out.analyses.push_back(std::move(r)); }); // --- Types --- out.types.clear(); query(db, "SELECT id, name, lang, domain, algebraic, description FROM types ORDER BY name", [&](sqlite3_stmt* s) { TypeRow r; r.id = col_str(s, 0); r.name = col_str(s, 1); r.lang = col_str(s, 2); r.domain = col_str(s, 3); r.algebraic = col_str(s, 4); r.description = col_str(s, 5); out.types.push_back(std::move(r)); }); sqlite3_close(db); out.prepare_chart_data(); return true; }