feat(resource-manager): added working singleton resource manager
all external data should now be handled through the resource manager. This will take care of location on disk as well as ownership
This commit is contained in:
40
src/resource/meson.build
Normal file
40
src/resource/meson.build
Normal file
@@ -0,0 +1,40 @@
|
||||
# Define the library
|
||||
resourceManager_sources = files(
|
||||
'private/resourceManager.cpp',
|
||||
'private/resourceManagerTypes.cpp'
|
||||
)
|
||||
|
||||
resourceManager_headers = files(
|
||||
'public/resourceManager.h',
|
||||
'public/resourceManagerTypes.h'
|
||||
)
|
||||
|
||||
dependencies = [
|
||||
yaml_cpp_dep,
|
||||
opatio_dep,
|
||||
eos_dep,
|
||||
quill_dep,
|
||||
config_dep,
|
||||
probe_dep,
|
||||
mfem_dep,
|
||||
macros_dep,
|
||||
meshio_dep
|
||||
]
|
||||
|
||||
libResourceHeader_dep = declare_dependency(include_directories: include_directories('public'))
|
||||
# Define the libresourceManager library so it can be linked against by other parts of the build system
|
||||
libresourceManager = static_library('resourceManager',
|
||||
resourceManager_sources,
|
||||
include_directories: include_directories('public'),
|
||||
cpp_args: ['-fvisibility=default'],
|
||||
dependencies: dependencies,
|
||||
install : true)
|
||||
|
||||
resourceManager_dep = declare_dependency(
|
||||
include_directories: include_directories('public'),
|
||||
link_with: libresourceManager,
|
||||
dependencies: dependencies
|
||||
)
|
||||
|
||||
# Make headers accessible
|
||||
install_headers(resourceManager_headers, subdir : '4DSSE/resource')
|
||||
68
src/resource/private/resourceManager.cpp
Normal file
68
src/resource/private/resourceManager.cpp
Normal file
@@ -0,0 +1,68 @@
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
|
||||
#include "quill/LogMacros.h"
|
||||
|
||||
#include "resourceManager.h"
|
||||
#include "resourceManagerTypes.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#define STRINGIFY(x) #x
|
||||
#define TOSTRING(x) STRINGIFY(x)
|
||||
|
||||
ResourceManager::ResourceManager() {
|
||||
std::string defaultDataDir = TOSTRING(DATA_DIR);
|
||||
m_dataDir = m_config.get<std::string>("Data:Dir", defaultDataDir);
|
||||
// -- Get the index file path using filesytem to make it a system safe path
|
||||
std::string indexFilePath = m_dataDir + "/index.yaml";
|
||||
std::filesystem::path indexFile(indexFilePath);
|
||||
|
||||
m_resourceConfig.loadConfig(indexFile.string());
|
||||
std::vector<std::string> assets = m_resourceConfig.keys();
|
||||
for (auto key : assets ) {
|
||||
load(key);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::vector<std::string> ResourceManager::getAvaliableResources() {
|
||||
std::vector<std::string> resources;
|
||||
resources = m_resourceConfig.keys();
|
||||
return resources;
|
||||
}
|
||||
|
||||
const Resource& ResourceManager::getResource(const std::string &name) const {
|
||||
auto it = m_resources.find(name);
|
||||
if (it != m_resources.end()) {
|
||||
return it->second;
|
||||
}
|
||||
throw std::runtime_error("Resource " + name + " not found");
|
||||
}
|
||||
|
||||
|
||||
bool ResourceManager::loadResource(std::string& name) {
|
||||
return load(name);
|
||||
}
|
||||
|
||||
bool ResourceManager::load(const std::string& name) {
|
||||
const std::string resourcePath = m_dataDir + "/" + m_resourceConfig.get<std::string>(name);
|
||||
std::filesystem::path resourceFile(resourcePath);
|
||||
if (!std::filesystem::exists(resourceFile)) {
|
||||
LOG_ERROR(m_logger, "Resource file not found: {}", resourceFile.string());
|
||||
return false;
|
||||
}
|
||||
LOG_INFO(m_logger, "Loading resource: {}", resourceFile.string());
|
||||
if (m_resources.find(name) != m_resources.end()) {
|
||||
LOG_INFO(m_logger, "Resource already loaded: {}", name);
|
||||
return true;
|
||||
}
|
||||
Resource resource = createResource(name, resourcePath);
|
||||
m_resources[name] = std::move(resource);
|
||||
// -- Check if the resource is already in the map
|
||||
return true;
|
||||
}
|
||||
|
||||
41
src/resource/private/resourceManagerTypes.cpp
Normal file
41
src/resource/private/resourceManagerTypes.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#include <string>
|
||||
|
||||
#include "resourceManagerTypes.h"
|
||||
#include "opatIO.h"
|
||||
#include "meshIO.h"
|
||||
#include "eosIO.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
std::string getFirstSegment(const std::string& input) {
|
||||
size_t pos = input.find(':');
|
||||
if (pos == std::string::npos) {
|
||||
// No colon found, return the entire string
|
||||
return input;
|
||||
} else {
|
||||
// Return substring from start to the position of the first colon
|
||||
return input.substr(0, pos);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Resource createResource(const std::string& type, const std::string& path) {
|
||||
static const std::unordered_map<std::string, std::function<Resource(const std::string&)>> factoryMap = {
|
||||
{"opac", [](const std::string& p) { return Resource(
|
||||
std::make_unique<OpatIO>(p));
|
||||
}},
|
||||
{"mesh", [](const std::string& p) { return Resource(
|
||||
std::make_unique<MeshIO>(p));
|
||||
}},
|
||||
{"eos", [](const std::string& p) { return Resource(
|
||||
std::make_unique<EosIO>(p));
|
||||
}}
|
||||
// Add more mappings as needed
|
||||
};
|
||||
auto it = factoryMap.find(getFirstSegment(type));
|
||||
if (it != factoryMap.end()) {
|
||||
return it->second(path);
|
||||
} else {
|
||||
throw std::invalid_argument("Unknown resource type: " + type);
|
||||
}
|
||||
}
|
||||
47
src/resource/public/resourceManager.h
Normal file
47
src/resource/public/resourceManager.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#ifndef RESOURCE_MANAGER_H
|
||||
#define RESOURCE_MANAGER_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "resourceManagerTypes.h"
|
||||
#include "config.h"
|
||||
#include "probe.h"
|
||||
#include "quill/LogMacros.h"
|
||||
|
||||
class ResourceManager {
|
||||
private:
|
||||
ResourceManager();
|
||||
ResourceManager(const ResourceManager&) = delete;
|
||||
ResourceManager& operator=(const ResourceManager&) = delete;
|
||||
Config& m_config = Config::getInstance();
|
||||
Probe::LogManager& m_logManager = Probe::LogManager::getInstance();
|
||||
quill::Logger* m_logger = m_logManager.getLogger("log");
|
||||
|
||||
Config m_resourceConfig;
|
||||
std::string m_dataDir;
|
||||
std::unordered_map<std::string, Resource> m_resources;
|
||||
|
||||
bool load(const std::string& name);
|
||||
|
||||
public:
|
||||
static ResourceManager& getInstance() {
|
||||
static ResourceManager instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
std::vector<std::string> getAvaliableResources();
|
||||
|
||||
const Resource& getResource(const std::string &name) const;
|
||||
|
||||
bool loadResource(std::string& name);
|
||||
|
||||
std::unordered_map<std::string, bool> loadAllResources();
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif // RESOURCE_MANAGER_H
|
||||
22
src/resource/public/resourceManagerTypes.h
Normal file
22
src/resource/public/resourceManagerTypes.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#ifndef RESOURCE_MANAGER_TYPES_H
|
||||
#define RESOURCE_MANAGER_TYPES_H
|
||||
|
||||
#include <memory>
|
||||
#include <variant>
|
||||
|
||||
#include "opatIO.h"
|
||||
#include "helm.h"
|
||||
#include "meshIO.h"
|
||||
#include "eosIO.h"
|
||||
|
||||
// -- Valid resource types
|
||||
using Resource = std::variant<
|
||||
std::unique_ptr<OpatIO>,
|
||||
std::unique_ptr<MeshIO>,
|
||||
std::unique_ptr<EosIO>>;
|
||||
|
||||
|
||||
std::string getFirstSegment(const std::string& input);
|
||||
Resource createResource(const std::string& type, const std::string& path);
|
||||
|
||||
#endif // RESOURCE_MANAGER_TYPES_H
|
||||
24
tests/resource/meson.build
Normal file
24
tests/resource/meson.build
Normal file
@@ -0,0 +1,24 @@
|
||||
# Test files for const
|
||||
test_sources = [
|
||||
'resourceManagerTest.cpp',
|
||||
]
|
||||
|
||||
foreach test_file : test_sources
|
||||
exe_name = test_file.split('.')[0]
|
||||
message('Building test: ' + exe_name)
|
||||
|
||||
# Create an executable target for each test
|
||||
test_exe = executable(
|
||||
exe_name,
|
||||
test_file,
|
||||
dependencies: [gtest_dep, resourceManager_dep, gtest_main, macros_dep],
|
||||
include_directories: include_directories('../../src/resource/public'),
|
||||
install_rpath: '@loader_path/../../src' # Ensure runtime library path resolves correctly
|
||||
)
|
||||
|
||||
# Add the executable as a test
|
||||
test(
|
||||
exe_name,
|
||||
test_exe,
|
||||
env: ['MESON_SOURCE_ROOT=' + meson.project_source_root(), 'MESON_BUILD_ROOT=' + meson.project_build_root()])
|
||||
endforeach
|
||||
61
tests/resource/resourceManagerTest.cpp
Normal file
61
tests/resource/resourceManagerTest.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include "resourceManager.h"
|
||||
#include "config.h"
|
||||
#include "eosIO.h"
|
||||
#include "helm.h"
|
||||
#include "resourceManagerTypes.h"
|
||||
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
/**
|
||||
* @file configTest.cpp
|
||||
* @brief Unit tests for the resourceManager class.
|
||||
*/
|
||||
|
||||
|
||||
std::string TEST_CONFIG = std::string(getenv("MESON_SOURCE_ROOT")) + "/tests/testsConfig.yaml";
|
||||
/**
|
||||
* @brief Test suite for the resourceManager class.
|
||||
*/
|
||||
class resourceManagerTest : public ::testing::Test {};
|
||||
|
||||
/**
|
||||
* @brief Test the constructor of the resourceManager class.
|
||||
*/
|
||||
TEST_F(resourceManagerTest, constructor) {
|
||||
Config::getInstance().loadConfig(TEST_CONFIG);
|
||||
EXPECT_NO_THROW(ResourceManager::getInstance());
|
||||
}
|
||||
|
||||
TEST_F(resourceManagerTest, getAvaliableResources) {
|
||||
Config::getInstance().loadConfig(TEST_CONFIG);
|
||||
ResourceManager& rm = ResourceManager::getInstance();
|
||||
std::vector<std::string> resources = rm.getAvaliableResources();
|
||||
std::set<std::string> expected = {"eos:helm", "mesh:sphere"};
|
||||
std::set<std::string> actual(resources.begin(), resources.end());
|
||||
EXPECT_EQ(expected, actual);
|
||||
}
|
||||
|
||||
TEST_F(resourceManagerTest, getResource) {
|
||||
Config::getInstance().loadConfig(TEST_CONFIG);
|
||||
ResourceManager& rm = ResourceManager::getInstance();
|
||||
std::string name = "eos:helm";
|
||||
const Resource &r = rm.getResource(name);
|
||||
// BREAKPOINT();
|
||||
const auto &eos = std::get<std::unique_ptr<EosIO>>(r);
|
||||
EXPECT_EQ("helm", eos->getFormat());
|
||||
EOSTable &table = eos->getTable();
|
||||
|
||||
// -- Extract the Helm table from the EOSTable
|
||||
helmholtz::HELMTable &helmTable = *std::get<std::unique_ptr<helmholtz::HELMTable>>(table);
|
||||
EXPECT_DOUBLE_EQ(helmTable.f[0][0], -1692098915534.8142);
|
||||
|
||||
EXPECT_THROW(rm.getResource("opac:GS98:high:doesNotExist"), std::runtime_error);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user