MetaCore/Source/MetaCoreFoundation/Private/MetaCoreProject.cpp

198 lines
7.0 KiB
C++

#include "MetaCoreFoundation/MetaCoreProject.h"
#include <array>
#include <fstream>
#include <regex>
#include <sstream>
namespace MetaCore {
namespace {
[[nodiscard]] std::string MetaCorePathToPortableString(const std::filesystem::path& path) {
return path.lexically_normal().generic_string();
}
[[nodiscard]] std::optional<std::string> MetaCoreReadTextFile(const std::filesystem::path& path) {
std::ifstream input(path);
if (!input.is_open()) {
return std::nullopt;
}
std::ostringstream stream;
stream << input.rdbuf();
return stream.str();
}
[[nodiscard]] std::optional<std::string> MetaCoreFindJsonStringValue(
const std::string& document,
const std::string& key
) {
const std::regex pattern("\\\"" + key + "\\\"\\s*:\\s*\\\"([^\\\"]*)\\\"");
std::smatch match;
if (!std::regex_search(document, match, pattern)) {
return std::nullopt;
}
return match[1].str();
}
[[nodiscard]] std::vector<std::string> MetaCoreFindJsonStringArray(
const std::string& document,
const std::string& key
) {
const std::regex arrayPattern("\\\"" + key + "\\\"\\s*:\\s*\\[([^\\]]*)\\]");
std::smatch arrayMatch;
if (!std::regex_search(document, arrayMatch, arrayPattern)) {
return {};
}
std::vector<std::string> results;
const std::regex stringPattern("\\\"([^\\\"]*)\\\"");
const std::string arrayBody = arrayMatch[1].str();
for (std::sregex_iterator iterator(arrayBody.begin(), arrayBody.end(), stringPattern), end; iterator != end; ++iterator) {
results.push_back((*iterator)[1].str());
}
return results;
}
[[nodiscard]] std::string MetaCoreEscapeJsonString(const std::string& value) {
std::string escaped;
escaped.reserve(value.size());
for (char character : value) {
switch (character) {
case '\\':
escaped += "\\\\";
break;
case '"':
escaped += "\\\"";
break;
default:
escaped += character;
break;
}
}
return escaped;
}
} // namespace
std::filesystem::path MetaCoreGetProjectFilePath(const std::filesystem::path& projectRoot) {
return projectRoot / "MetaCore.project.json";
}
std::optional<std::filesystem::path> MetaCoreReadProjectRootFromEnvironment() {
char* projectPathRaw = nullptr;
std::size_t projectPathLength = 0;
if (_dupenv_s(&projectPathRaw, &projectPathLength, "METACORE_PROJECT_PATH") != 0 || projectPathRaw == nullptr) {
return std::nullopt;
}
std::filesystem::path projectPath(projectPathRaw);
free(projectPathRaw);
if (projectPath.filename() == "MetaCore.project.json") {
projectPath = projectPath.parent_path();
}
if (std::filesystem::exists(MetaCoreGetProjectFilePath(projectPath))) {
std::error_code errorCode;
return std::filesystem::weakly_canonical(projectPath, errorCode);
}
return std::nullopt;
}
std::optional<std::filesystem::path> MetaCoreDiscoverProjectRoot(const std::filesystem::path& startDirectory) {
if (const auto environmentProjectRoot = MetaCoreReadProjectRootFromEnvironment(); environmentProjectRoot.has_value()) {
return environmentProjectRoot;
}
std::error_code errorCode;
std::filesystem::path current = std::filesystem::weakly_canonical(startDirectory, errorCode);
if (current.empty()) {
current = startDirectory;
}
while (!current.empty()) {
const std::array<std::filesystem::path, 3> candidates = {
current,
current / "SandboxProject",
current / "TestProject"
};
for (const auto& candidate : candidates) {
if (std::filesystem::exists(MetaCoreGetProjectFilePath(candidate))) {
return std::filesystem::weakly_canonical(candidate, errorCode);
}
}
const std::filesystem::path parent = current.parent_path();
if (parent == current) {
break;
}
current = parent;
}
return std::nullopt;
}
std::optional<MetaCoreProjectFileDocument> MetaCoreReadProjectFile(const std::filesystem::path& projectFilePath) {
const auto projectDocument = MetaCoreReadTextFile(projectFilePath);
if (!projectDocument.has_value()) {
return std::nullopt;
}
MetaCoreProjectFileDocument document;
if (const auto name = MetaCoreFindJsonStringValue(*projectDocument, "name"); name.has_value()) {
document.Name = *name;
}
if (const auto version = MetaCoreFindJsonStringValue(*projectDocument, "version"); version.has_value()) {
document.Version = *version;
}
if (const auto startupScene = MetaCoreFindJsonStringValue(*projectDocument, "startup_scene"); startupScene.has_value()) {
document.StartupScenePath = std::filesystem::path(*startupScene).lexically_normal();
}
if (const auto runtimeDir = MetaCoreFindJsonStringValue(*projectDocument, "runtime_directory"); runtimeDir.has_value()) {
document.RuntimeDirectory = std::filesystem::path(*runtimeDir).lexically_normal();
}
if (const auto uiDir = MetaCoreFindJsonStringValue(*projectDocument, "ui_directory"); uiDir.has_value()) {
document.UiDirectory = std::filesystem::path(*uiDir).lexically_normal();
}
if (const auto buildDir = MetaCoreFindJsonStringValue(*projectDocument, "build_directory"); buildDir.has_value()) {
document.BuildDirectory = std::filesystem::path(*buildDir).lexically_normal();
}
for (const std::string& scenePath : MetaCoreFindJsonStringArray(*projectDocument, "scenes")) {
document.ScenePaths.push_back(std::filesystem::path(scenePath).lexically_normal());
}
return document;
}
bool MetaCoreWriteProjectFile(
const std::filesystem::path& projectFilePath,
const MetaCoreProjectFileDocument& document
) {
std::ofstream output(projectFilePath, std::ios::trunc);
if (!output.is_open()) {
return false;
}
output << "{\n";
output << " \"name\": \"" << MetaCoreEscapeJsonString(document.Name) << "\",\n";
output << " \"version\": \"" << MetaCoreEscapeJsonString(document.Version) << "\",\n";
output << " \"runtime_directory\": \"" << MetaCoreEscapeJsonString(MetaCorePathToPortableString(document.RuntimeDirectory)) << "\",\n";
output << " \"ui_directory\": \"" << MetaCoreEscapeJsonString(MetaCorePathToPortableString(document.UiDirectory)) << "\",\n";
output << " \"build_directory\": \"" << MetaCoreEscapeJsonString(MetaCorePathToPortableString(document.BuildDirectory)) << "\",\n";
output << " \"startup_scene\": \"" << MetaCoreEscapeJsonString(MetaCorePathToPortableString(document.StartupScenePath)) << "\",\n";
output << " \"scenes\": [\n";
for (std::size_t index = 0; index < document.ScenePaths.size(); ++index) {
output << " \"" << MetaCoreEscapeJsonString(MetaCorePathToPortableString(document.ScenePaths[index])) << "\"";
output << (index + 1 < document.ScenePaths.size() ? ",\n" : "\n");
}
output << " ]\n";
output << "}\n";
return true;
}
} // namespace MetaCore