338 lines
13 KiB
C++
338 lines
13 KiB
C++
#include "Config.h"
|
|
|
|
|
|
/**
|
|
* @brief General exception class for the Conf class.
|
|
*
|
|
* This exception class can be thrown to indicate an error in the Conf class. It provides an error message describing the problem.
|
|
*/
|
|
class ConfigLoadException: public std::exception
|
|
{
|
|
public:
|
|
/**
|
|
* @brief Constructor that takes a C-style string error message.
|
|
*
|
|
* The string contents are copied upon construction. The caller is responsible for deleting the char*.
|
|
*
|
|
* @param message C-style string error message.
|
|
*/
|
|
explicit ConfigLoadException(const char* message):
|
|
msg_(message)
|
|
{}
|
|
|
|
/**
|
|
* @brief Constructor that takes a C++ STL string error message.
|
|
*
|
|
* @param message The error message.
|
|
*/
|
|
explicit ConfigLoadException(const std::string& message):
|
|
msg_(message)
|
|
{}
|
|
|
|
/**
|
|
* @brief Virtual destructor to allow for subclassing.
|
|
*/
|
|
virtual ~ConfigLoadException() throw (){}
|
|
|
|
/**
|
|
* @brief Returns a pointer to the error description.
|
|
*
|
|
* @return A pointer to a const char*. The underlying memory is in posession of the Exception object. Callers must not attempt to free the memory.
|
|
*/
|
|
virtual const char* what() const throw (){
|
|
return msg_.c_str();
|
|
}
|
|
|
|
protected:
|
|
/**
|
|
* @brief Error message.
|
|
*/
|
|
std::string msg_;
|
|
};
|
|
|
|
|
|
/**
|
|
* @brief Set the string value of a key
|
|
*
|
|
* This method sets the value of a key as a string in a member variable.
|
|
* It first retrieves the string value of the key using the `get_string` method.
|
|
* If the key is not found, a `ConfigLoadException` is thrown.
|
|
* If the key is found, the string value is interpolated using the `interpolate` method and then assigned to the member variable.
|
|
* The method logs the task in the log file using the `slog.log_task` method.
|
|
*
|
|
* @param keyname The name of the key to retrieve
|
|
* @param object_member The reference to the member variable to store the value
|
|
* @param filename The name of the configuration file
|
|
*
|
|
* @throws ConfigLoadException If the key is not found in the configuration file
|
|
*/
|
|
void Conf::set_object_s(std::string keyname, std::string & object_member, std::string filename )
|
|
{
|
|
std::string jval_s;
|
|
if ( this->get_string(jval_s, keyname) != 0) {
|
|
throw ConfigLoadException( "'" + keyname + "' string is not set in the config file supplied: " + filename );
|
|
} else {
|
|
interpolate(jval_s);
|
|
object_member = jval_s;
|
|
}
|
|
this->slog.log_task( E_DEBUG, "SET_PROPERTY", "'" + keyname + "': " + object_member );
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief Set the string value of a key with a derived path
|
|
*
|
|
* This method sets the value of a key as a string in a member variable with a derived path.
|
|
* It first retrieves the string value of the key using the `get_string` method.
|
|
* If the key is not found, a `ConfigLoadException` is thrown.
|
|
* If the key is found, the string value is interpolated using the `interpolate` method and then passed to the `prepend_project_root` method to derive the path.
|
|
* The derived path is then assigned to the member variable.
|
|
* The method logs the task in the log file using the `slog.log_task` method.
|
|
*
|
|
* @param keyname The name of the key to retrieve
|
|
* @param object_member The reference to the member variable to store the value
|
|
* @param filename The name of the configuration file
|
|
*
|
|
* @throws ConfigLoadException If the key is not found in the configuration file
|
|
*/
|
|
void Conf::set_object_s_derivedpath(std::string keyname, std::string & object_member, std::string filename )
|
|
{
|
|
std::string jval_s;
|
|
if ( this->get_string(jval_s, keyname) != 0) {
|
|
throw ConfigLoadException( "'" + keyname + "' string is not set in the config file supplied: " + filename );
|
|
} else {
|
|
interpolate(jval_s);
|
|
object_member = prepend_project_root( jval_s );
|
|
}
|
|
this->slog.log_task( E_DEBUG, "SET_PROPERTY", "'" + keyname + "': " + object_member );
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief Set the boolean value of a key
|
|
*
|
|
* This method sets the value of a key as a boolean in a member variable.
|
|
* It first retrieves the boolean value of the key using the `get_bool` method.
|
|
* If the key is not found, a `ConfigLoadException` is thrown.
|
|
* If the key is found, the boolean value is assigned to the member variable.
|
|
* The method logs the task in the log file using the `slog.log_task` method.
|
|
*
|
|
* @param keyname The name of the key to retrieve
|
|
* @param object_member The reference to the member variable to store the value
|
|
* @param filename The name of the configuration file
|
|
*
|
|
* @throws ConfigLoadException If the key is not found in the configuration file
|
|
*/
|
|
void Conf::set_object_b(std::string keyname, bool & object_member, std::string filename )
|
|
{
|
|
bool jval_b;
|
|
if ( this->get_bool(jval_b, keyname) != 0) {
|
|
throw ConfigLoadException( "'" + keyname + "' boolean is not set in the config file supplied: " + filename );
|
|
} else {
|
|
object_member = jval_b;
|
|
}
|
|
this->slog.log_task( E_DEBUG, "SET_PROPERTY", "'" + keyname + "' " + std::to_string(object_member));
|
|
}
|
|
|
|
void removeTrailingSlash(std::string &str) {
|
|
if (!str.empty() && str.back() == '/') {
|
|
str.pop_back();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Prepend the project root to a relative path
|
|
*
|
|
* This method prepends the project root to a given relative path.
|
|
* The project root is stored as a member variable in the `Conf` object.
|
|
* The method concatenates the project root and the relative path separated by a forward slash (/) and returns the result.
|
|
*
|
|
* @param relative_path The relative path to prepend the project root to
|
|
*
|
|
* @return The concatenated path with the project root and the relative path
|
|
*/
|
|
std::string Conf::prepend_project_root( std::string relative_path)
|
|
{
|
|
removeTrailingSlash(relative_path);
|
|
return this->project_root + "/" + relative_path;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* @brief Check if a path exists
|
|
*
|
|
* This method checks if a given path exists.
|
|
* If the path exists, a log entry is made in the log file using the `slog.log_task` method with log level `E_DEBUG`.
|
|
* If the path does not exist, a log entry is made in the log file with log level `E_FATAL` and a `ConfigLoadException` is thrown.
|
|
*
|
|
* @param keyname The name of the key to be used in the log entry
|
|
* @param path The path to be checked
|
|
*
|
|
* @throws ConfigLoadException If the path does not exist
|
|
*/
|
|
void Conf::checkPathExists( std::string keyname, const std::string &path ) {
|
|
if ( exists( path ) ) {
|
|
this->slog.log_task( E_DEBUG, "SANITY_CHECKS", "'" + keyname + "' exists ('" + path + "')" );
|
|
} else {
|
|
this->slog.log_task( E_FATAL, "SANITY_CHECKS", "'" + keyname + "' does not exist ('" + path + "')" );
|
|
throw ConfigLoadException("Path does not exist.");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Load the shells
|
|
*
|
|
* This method loads the shell definitions from a file.
|
|
* The path to the shell definition file is stored as a member variable in the `Conf` object.
|
|
* The method first logs the task of loading the shells using the `slog.log_task` method.
|
|
* The method then tries to load the JSON file using the `load_json_file` method.
|
|
* If an exception occurs while loading the file, a log entry with log level `E_FATAL` is made and a `ConfigLoadException` is thrown.
|
|
* The method then retrieves the serialized value of the "shells" key using the `get_serialized` method.
|
|
* If the "shells" key is not found, a log entry with log level `E_FATAL` is made and a `ConfigLoadException` is thrown.
|
|
* The method then loops through the serialized values, loads each shell definition using the `load_root` method of the `Shell` class, and stores each shell in the `shells` vector.
|
|
* The method logs each loaded shell using the `slog.log_task` method.
|
|
*
|
|
* @throws ConfigLoadException If there is an error parsing the shell definition file
|
|
*/
|
|
void Conf::load_shells() {
|
|
this->slog.log_task( E_DEBUG, "SHELLS", "Loading shells..." );
|
|
try {
|
|
// load the test file.
|
|
this->load_json_file( this->shell_definitions_path );
|
|
} catch (std::exception& e) {
|
|
this->slog.log_task( E_FATAL, "SHELLS", "Unable to load shell definition file: '" + this->shell_definitions_path + "'. Error: " + e.what());
|
|
throw ConfigLoadException("Parsing error in shell definitions file.");
|
|
}
|
|
Json::Value jbuff;
|
|
|
|
if ( this-> get_serialized( jbuff, "shells" ) != 0 ) {
|
|
this->slog.log_task( E_FATAL, "SHELLS", "Parsing error: '" + this->shell_definitions_path + "'. Error: 'shells' key not found." );
|
|
throw ConfigLoadException("Parsing error in shell definitions file.");
|
|
}
|
|
|
|
Shell tmp_S = Shell( this->LOG_LEVEL );
|
|
for ( int index = 0; index < jbuff.size(); index++ )
|
|
{
|
|
tmp_S.load_root( jbuff[index] );
|
|
this->shells.push_back( tmp_S );
|
|
this->slog.log_task( E_DEBUG, "SHELLS", "Loaded shell: '" + tmp_S.name + "' (" + tmp_S.path + ")" );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Get a shell by name
|
|
*
|
|
* This method retrieves a `Shell` object by its name from the `shells` vector.
|
|
* The method loops through the `shells` vector and compares each shell's name to the input name.
|
|
* If a match is found, the matching shell is returned.
|
|
* If no match is found, a `ConfigLoadException` is thrown.
|
|
*
|
|
* @param name The name of the shell to retrieve
|
|
*
|
|
* @return The `Shell` object corresponding to the input name
|
|
*
|
|
* @throws ConfigLoadException If the input name does not match any shell in the `shells` vector
|
|
*/
|
|
Shell Conf::get_shell_by_name( std::string name ) {
|
|
for (auto &shell: this->shells) {
|
|
if (shell.name == name) {
|
|
return shell;
|
|
}
|
|
}
|
|
throw ConfigLoadException("The shell specified ('" + name + "') is not defined in the shell definitions file.");
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* @class Conf
|
|
* @brief Loads the configuration for the application
|
|
*
|
|
* This class is responsible for loading the configuration for the application. It uses the `JSON_Loader` class to
|
|
* parse the JSON configuration file and stores the relevant information.
|
|
*
|
|
* @note Currently, the class only supports loading a single configuration file. However, it can be expanded to
|
|
* support loading multiple files or detecting when a directory path is supplied for the units_path or plan_path and
|
|
* importing all Tasks and Units.
|
|
*
|
|
* @param filename - The filename to load the configuration from.
|
|
* @param LOG_LEVEL - The log level to use for logging messages.
|
|
*/
|
|
Conf::Conf(std::string filename, int LOG_LEVEL ): JSON_Loader(LOG_LEVEL ), slog(LOG_LEVEL, "_conf_" )
|
|
{
|
|
this->LOG_LEVEL = LOG_LEVEL;
|
|
this->slog.log_task( E_DEBUG, "LOAD", "Loading configuration file: " + filename );
|
|
|
|
interpolate( filename );
|
|
|
|
try {
|
|
// load the test file.
|
|
this->load_json_file( filename );
|
|
} catch (std::exception& e) {
|
|
this->slog.log( E_FATAL, "Unable to load configuration file: '" + filename + "'. Error: " + e.what());
|
|
throw ConfigLoadException("Parsing error in configuration file.");
|
|
}
|
|
Json::Value jbuff;
|
|
|
|
if ( this->get_serialized( jbuff, "config" ) != 0) {
|
|
this->slog.log_task( E_FATAL, "LOAD", "Unable to locate 'config' object in configuration file: " + filename );
|
|
throw ConfigLoadException("Unable to locate 'config' object in configuration file.");
|
|
} else {
|
|
this->slog.log_task( E_DEBUG, "LOAD", "Found 'config' object in configuration file: " + filename );
|
|
this->json_root = jbuff;
|
|
}
|
|
|
|
set_object_s( "project_root", this->project_root, filename );
|
|
interpolate( project_root );
|
|
// convert to an absolute path after all the interpolation is done.
|
|
this->project_root = get_absolute_path( this->project_root );
|
|
|
|
set_object_s( "logs_path", this->logs_path, filename );
|
|
interpolate( this->logs_path );
|
|
|
|
// all other paths are relative to project_root
|
|
set_object_s_derivedpath( "units_path", this->units_path, filename );
|
|
interpolate( this->units_path );
|
|
|
|
set_object_s_derivedpath( "shells_path", this->shell_definitions_path, filename );
|
|
interpolate( this->shell_definitions_path );
|
|
|
|
// ensure these paths exists, with exception to the logs_path, which will be created at runtime
|
|
this->slog.log_task( E_DEBUG, "SANITY_CHECKS", "Checking for sanity..." );
|
|
checkPathExists( "project_root", this->project_root );
|
|
checkPathExists( "units_path", this->units_path );
|
|
checkPathExists( "shells_path", this->shell_definitions_path );
|
|
|
|
// shells are scoped beyond plan so they need to be considered part of config
|
|
load_shells();
|
|
|
|
this->slog.log_task( E_DEBUG, "LOAD", "CONFIGURATION LOADED." );
|
|
}
|
|
|
|
/**
|
|
* @brief Gets the path to the Unit definition file
|
|
*
|
|
* This function returns the path to the Unit definition file that was specified in the configuration file.
|
|
*
|
|
* @return The path to the Unit definition file.
|
|
*/
|
|
std::string Conf::get_units_path() { return this->units_path; }
|
|
|
|
/**
|
|
* @brief Gets the path to the logs directory
|
|
*
|
|
* This function returns the path to the logs directory that was specified in the configuration file.
|
|
*
|
|
* @return The path to the logs directory.
|
|
*/
|
|
std::string Conf::get_logs_path() { return this->logs_path; }
|
|
|
|
/**
|
|
* @brief Gets the project root directory
|
|
*
|
|
* This function returns the path to the project root directory that was specified in the configuration file.
|
|
*
|
|
* @return The path to the project root directory.
|
|
*/
|
|
std::string Conf::get_project_root() { return this->project_root; } |