Addded Romeo that generates some doc. WIP.
This commit is contained in:
@@ -0,0 +1,300 @@
|
||||
#include "Database.h"
|
||||
|
||||
#include <Core/HAL/Filesystem/Filesystem.h>
|
||||
#include <Core/Memory/MemoryArena.h>
|
||||
#include <Windows.h>
|
||||
|
||||
namespace Romeo
|
||||
{
|
||||
void DB_Init(Database& db, Juliet::Arena* arena)
|
||||
{
|
||||
printf("Entering DB_Init...\n");
|
||||
db.DbArena = arena;
|
||||
db.Entries.Create(arena JULIET_DEBUG_ONLY(, "RomeoDB"));
|
||||
printf("Exiting DB_Init...\n");
|
||||
}
|
||||
|
||||
struct ScanContext
|
||||
{
|
||||
Database* Db;
|
||||
Juliet::Arena* TempArena;
|
||||
};
|
||||
|
||||
static void ProcessFile(ScanContext& ctx, const Juliet::String& path, const WIN32_FIND_DATAW& findData)
|
||||
{
|
||||
// Simple filter: only .h and .cpp
|
||||
const char* pPath = CStr(path);
|
||||
size_t len = StringLength(path);
|
||||
|
||||
if (len < 3)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool isHeader = false;
|
||||
bool isCpp = false;
|
||||
|
||||
if (pPath[len - 2] == '.' && pPath[len - 1] == 'h')
|
||||
{
|
||||
isHeader = true;
|
||||
}
|
||||
else if (len >= 4 && pPath[len - 4] == '.' && pPath[len - 3] == 'c' && pPath[len - 2] == 'p' && pPath[len - 1] == 'p')
|
||||
{
|
||||
isCpp = true;
|
||||
}
|
||||
|
||||
if (!isHeader && !isCpp)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t lastModified = (static_cast<uint64_t>(findData.ftLastWriteTime.dwHighDateTime) << 32) | findData.ftLastWriteTime.dwLowDateTime;
|
||||
|
||||
// Try to find if we already have it in the DB (ignoring \include\ or \src\ offset)
|
||||
const char* relStart = pPath;
|
||||
if (const char* p1 = strstr(pPath, "\\include\\"))
|
||||
{
|
||||
relStart = p1 + 9;
|
||||
}
|
||||
else if (const char* p2 = strstr(pPath, "\\src\\"))
|
||||
{
|
||||
relStart = p2 + 5;
|
||||
}
|
||||
|
||||
size_t baseLen = len - static_cast<size_t>(relStart - pPath);
|
||||
baseLen -= (isHeader ? 2 : 4);
|
||||
|
||||
FileEntry* entry = nullptr;
|
||||
for (FileEntry& e : ctx.Db->Entries)
|
||||
{
|
||||
Juliet::String dbPath = e.HeaderPath.Size > 0 ? e.HeaderPath : e.CppPath;
|
||||
if (dbPath.Size > 0)
|
||||
{
|
||||
const char* pDbPath = CStr(dbPath);
|
||||
const char* dbRelStart = pDbPath;
|
||||
if (const char* p1 = strstr(pDbPath, "\\include\\"))
|
||||
{
|
||||
dbRelStart = p1 + 9;
|
||||
}
|
||||
else if (const char* p2 = strstr(pDbPath, "\\src\\"))
|
||||
{
|
||||
dbRelStart = p2 + 5;
|
||||
}
|
||||
|
||||
bool dbIsHeader = (pDbPath[dbPath.Size - 1] == 'h');
|
||||
size_t dbBaseLen = dbPath.Size - static_cast<size_t>(dbRelStart - pDbPath);
|
||||
dbBaseLen -= (dbIsHeader ? 2 : 4);
|
||||
|
||||
if (baseLen == dbBaseLen && strncmp(relStart, dbRelStart, baseLen) == 0)
|
||||
{
|
||||
entry = &e;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!entry)
|
||||
{
|
||||
FileEntry newEntry = {};
|
||||
ctx.Db->Entries.PushBack(newEntry);
|
||||
entry = ctx.Db->Entries.Back();
|
||||
}
|
||||
|
||||
if (isHeader)
|
||||
{
|
||||
if (entry->HeaderPath.Size == 0)
|
||||
{
|
||||
entry->HeaderPath = Juliet::StringCopy(ctx.Db->DbArena, path);
|
||||
}
|
||||
entry->LastModifiedHeader = lastModified;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (entry->CppPath.Size == 0)
|
||||
{
|
||||
entry->CppPath = Juliet::StringCopy(ctx.Db->DbArena, path);
|
||||
}
|
||||
entry->LastModifiedCpp = lastModified;
|
||||
}
|
||||
}
|
||||
|
||||
static void ScanDirectoryRecursive(ScanContext& ctx, const Juliet::String& currentDir)
|
||||
{
|
||||
size_t searchPathLen = currentDir.Size + 3; // "\*" + null
|
||||
char* searchPathBuf = ArenaPushArray<char>(ctx.TempArena, searchPathLen JULIET_DEBUG_PARAM("RomeoDbSearchPathBuf"));
|
||||
snprintf(searchPathBuf, searchPathLen, "%s\\*", CStr(currentDir));
|
||||
Juliet::String searchPath = {searchPathBuf, searchPathLen - 1};
|
||||
|
||||
// Convert to wide string for Win32
|
||||
int wLen = MultiByteToWideChar(CP_UTF8, 0, CStr(searchPath), -1, nullptr, 0);
|
||||
if (wLen <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
wchar_t* wSearchPath = ArenaPushArray<wchar_t>(ctx.TempArena, static_cast<size_t>(wLen) JULIET_DEBUG_PARAM("RomeoDbWSearch"));
|
||||
MultiByteToWideChar(CP_UTF8, 0, CStr(searchPath), -1, wSearchPath, wLen);
|
||||
|
||||
WIN32_FIND_DATAW findData;
|
||||
HANDLE hFind = FindFirstFileExW(wSearchPath, FindExInfoBasic, &findData, FindExSearchNameMatch, nullptr, 0);
|
||||
|
||||
if (hFind == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
if (wcscmp(findData.cFileName, L".") == 0 || wcscmp(findData.cFileName, L"..") == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Convert filename back to UTF8
|
||||
int uLen = WideCharToMultiByte(CP_UTF8, 0, findData.cFileName, -1, nullptr, 0, nullptr, nullptr);
|
||||
char* uFileName = ArenaPushArray<char>(ctx.TempArena, static_cast<size_t>(uLen) JULIET_DEBUG_PARAM("RomeoDbUFileName"));
|
||||
WideCharToMultiByte(CP_UTF8, 0, findData.cFileName, -1, uFileName, uLen, nullptr, nullptr);
|
||||
|
||||
size_t fullPathLen = currentDir.Size + 1 + static_cast<size_t>(uLen); // "\" + null
|
||||
char* fullPathBuf = ArenaPushArray<char>(ctx.TempArena, fullPathLen JULIET_DEBUG_PARAM("RomeoDbFullPathBuf"));
|
||||
snprintf(fullPathBuf, fullPathLen, "%s\\%s", CStr(currentDir), uFileName);
|
||||
Juliet::String fullPath = {fullPathBuf, fullPathLen - 1};
|
||||
|
||||
if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
{
|
||||
// Skip external/build dirs
|
||||
if (strcmp(uFileName, "External") != 0 && strcmp(uFileName, "Intermediate") != 0 && strcmp(uFileName, "bin") != 0 && strcmp(uFileName, ".git") != 0)
|
||||
{
|
||||
ScanDirectoryRecursive(ctx, fullPath);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ProcessFile(ctx, fullPath, findData);
|
||||
}
|
||||
} while (FindNextFileW(hFind, &findData) != 0);
|
||||
|
||||
FindClose(hFind);
|
||||
}
|
||||
|
||||
void DB_ScanWorkspace(Database& db, Juliet::Arena* tempArena, const Juliet::String& rootDir)
|
||||
{
|
||||
Assert(tempArena != nullptr);
|
||||
ScanContext ctx;
|
||||
ctx.Db = &db;
|
||||
ctx.TempArena = tempArena;
|
||||
|
||||
// Scan the src and include directories specifically to avoid trash
|
||||
size_t srcDirLen = rootDir.Size + 12; // "\Juliet\src" + null
|
||||
char* srcDirBuf = ArenaPushArray<char>(tempArena, srcDirLen JULIET_DEBUG_PARAM("RomeoDbSrcDirBuf"));
|
||||
snprintf(srcDirBuf, srcDirLen, "%s\\Juliet\\src", CStr(rootDir));
|
||||
Juliet::String srcDir = {srcDirBuf, srcDirLen - 1};
|
||||
|
||||
size_t incDirLen = rootDir.Size + 16; // "\Juliet\include" + null
|
||||
char* incDirBuf = ArenaPushArray<char>(tempArena, incDirLen JULIET_DEBUG_PARAM("RomeoDbIncDirBuf"));
|
||||
snprintf(incDirBuf, incDirLen, "%s\\Juliet\\include", CStr(rootDir));
|
||||
Juliet::String incDir = {incDirBuf, incDirLen - 1};
|
||||
|
||||
ScanDirectoryRecursive(ctx, srcDir);
|
||||
ScanDirectoryRecursive(ctx, incDir);
|
||||
}
|
||||
|
||||
bool DB_Save(const Database& db, const Juliet::String& filePath)
|
||||
{
|
||||
FILE* file = nullptr;
|
||||
if (fopen_s(&file, CStr(filePath), "wb") != 0 || !file)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t entryCount = db.Entries.Size();
|
||||
fwrite(&entryCount, sizeof(uint64_t), 1, file);
|
||||
|
||||
for (const FileEntry& entry : db.Entries)
|
||||
{
|
||||
uint64_t hLen = entry.HeaderPath.Size;
|
||||
fwrite(&hLen, sizeof(uint64_t), 1, file);
|
||||
if (hLen > 0) fwrite(entry.HeaderPath.Data, 1, static_cast<size_t>(hLen), file);
|
||||
|
||||
uint64_t cLen = entry.CppPath.Size;
|
||||
fwrite(&cLen, sizeof(uint64_t), 1, file);
|
||||
if (cLen > 0) fwrite(entry.CppPath.Data, 1, static_cast<size_t>(cLen), file);
|
||||
|
||||
fwrite(&entry.LastModifiedHeader, sizeof(uint64_t), 1, file);
|
||||
fwrite(&entry.LastModifiedCpp, sizeof(uint64_t), 1, file);
|
||||
fwrite(&entry.LastMarkdownCreatedTime, sizeof(uint64_t), 1, file);
|
||||
fwrite(&entry.LastAIGeneratedTime, sizeof(uint64_t), 1, file);
|
||||
|
||||
uint64_t dLen = entry.Description.Size;
|
||||
fwrite(&dLen, sizeof(uint64_t), 1, file);
|
||||
if (dLen > 0) fwrite(entry.Description.Data, 1, static_cast<size_t>(dLen), file);
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DB_Load(Database& db, const Juliet::String& filePath)
|
||||
{
|
||||
FILE* file = nullptr;
|
||||
if (fopen_s(&file, CStr(filePath), "rb") != 0 || !file)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
db.Entries.Clear();
|
||||
|
||||
uint64_t entryCount = 0;
|
||||
if (fread(&entryCount, sizeof(uint64_t), 1, file) != 1)
|
||||
{
|
||||
fclose(file);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint64_t i = 0; i < entryCount; ++i)
|
||||
{
|
||||
FileEntry entry = {};
|
||||
|
||||
uint64_t hLen = 0;
|
||||
if (fread(&hLen, sizeof(uint64_t), 1, file) != 1) break;
|
||||
if (hLen > 0)
|
||||
{
|
||||
entry.HeaderPath.Data = ArenaPushArray<char>(db.DbArena, static_cast<size_t>(hLen + 1) JULIET_DEBUG_PARAM("DbLoadHeader"));
|
||||
entry.HeaderPath.Size = static_cast<size_t>(hLen);
|
||||
fread(entry.HeaderPath.Data, 1, static_cast<size_t>(hLen), file);
|
||||
entry.HeaderPath.Data[hLen] = '\0';
|
||||
}
|
||||
|
||||
uint64_t cLen = 0;
|
||||
if (fread(&cLen, sizeof(uint64_t), 1, file) != 1) break;
|
||||
if (cLen > 0)
|
||||
{
|
||||
entry.CppPath.Data = ArenaPushArray<char>(db.DbArena, static_cast<size_t>(cLen + 1) JULIET_DEBUG_PARAM("DbLoadCpp"));
|
||||
entry.CppPath.Size = static_cast<size_t>(cLen);
|
||||
fread(entry.CppPath.Data, 1, static_cast<size_t>(cLen), file);
|
||||
entry.CppPath.Data[cLen] = '\0';
|
||||
}
|
||||
|
||||
fread(&entry.LastModifiedHeader, sizeof(uint64_t), 1, file);
|
||||
fread(&entry.LastModifiedCpp, sizeof(uint64_t), 1, file);
|
||||
fread(&entry.LastMarkdownCreatedTime, sizeof(uint64_t), 1, file);
|
||||
fread(&entry.LastAIGeneratedTime, sizeof(uint64_t), 1, file);
|
||||
|
||||
uint64_t dLen = 0;
|
||||
if (fread(&dLen, sizeof(uint64_t), 1, file) != 1) break;
|
||||
if (dLen > 0)
|
||||
{
|
||||
entry.Description.Data = ArenaPushArray<char>(db.DbArena, static_cast<size_t>(dLen + 1) JULIET_DEBUG_PARAM("DbLoadDesc"));
|
||||
entry.Description.Size = static_cast<size_t>(dLen);
|
||||
fread(entry.Description.Data, 1, static_cast<size_t>(dLen), file);
|
||||
entry.Description.Data[dLen] = '\0';
|
||||
}
|
||||
|
||||
db.Entries.PushBack(entry);
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Romeo
|
||||
Reference in New Issue
Block a user