1
0
mirror of https://github.com/rwengine/openrw.git synced 2024-09-18 16:32:32 +02:00

rwgame+config: show an error dialog when an error occurred on parsing the config file

This commit is contained in:
Anonymous Maarten 2017-02-18 16:46:40 +01:00 committed by Daniel Evans
parent 76f2665acf
commit e28e429b86
4 changed files with 135 additions and 34 deletions

View File

@ -17,8 +17,7 @@ GameBase::GameBase(Logger &inlog, int argc, char *argv[]) : log(inlog) {
+ config.getConfigFile() + "\".\n" + config.getConfigFile() + "\".\n"
+ "Adapt the following default INI to your configuration.\n" + "Adapt the following default INI to your configuration.\n"
+ config.getDefaultINIString()); + config.getDefaultINIString());
throw std::runtime_error("Invalid configuration file at: " + throw std::runtime_error(config.getParseResult().what());
config.getConfigFile());
} }
size_t w = kWindowWidth, h = kWindowHeight; size_t w = kWindowWidth, h = kWindowHeight;
@ -28,11 +27,12 @@ GameBase::GameBase(Logger &inlog, int argc, char *argv[]) : log(inlog) {
// Define and parse command line options // Define and parse command line options
namespace po = boost::program_options; namespace po = boost::program_options;
po::options_description desc("Available options"); po::options_description desc("Available options");
desc.add_options()("help", "Show this help message")( desc.add_options()(
"help", "Show this help message")(
"width,w", po::value<size_t>(), "Game resolution width in pixel")( "width,w", po::value<size_t>(), "Game resolution width in pixel")(
"height,h", po::value<size_t>(), "Game resolution height in pixel")( "height,h", po::value<size_t>(), "Game resolution height in pixel")(
"fullscreen,f", "Enable fullscreen mode")("newgame,n", "fullscreen,f", "Enable fullscreen mode")(
"Directly start a new game")( "newgame,n", "Directly start a new game")(
"test,t", "Starts a new game in a test location")( "test,t", "Starts a new game in a test location")(
"load,l", po::value<std::string>(), "Load save file")( "load,l", po::value<std::string>(), "Load save file")(
"benchmark,b", po::value<std::string>(), "Run benchmark from file"); "benchmark,b", po::value<std::string>(), "Run benchmark from file");

View File

@ -130,7 +130,7 @@ GameConfig::ParseResult GameConfig::parseConfig(
ParseType destType, std::string &destination) ParseType destType, std::string &destination)
{ {
pt::ptree srcTree; pt::ptree srcTree;
ParseResult parseResult; ParseResult parseResult(srcType, source, destType, destination);
try { try {
if (srcType == ParseType::STRING) { if (srcType == ParseType::STRING) {
@ -140,7 +140,7 @@ GameConfig::ParseResult GameConfig::parseConfig(
} }
} catch (pt::ini_parser_error &e) { } catch (pt::ini_parser_error &e) {
// Catches illegal input files (nonsensical input, duplicate keys) // Catches illegal input files (nonsensical input, duplicate keys)
parseResult.failInputFile(e.filename(), e.line(), e.message()); parseResult.failInputFile(e.line(), e.message());
RW_MESSAGE(e.what()); RW_MESSAGE(e.what());
return parseResult; return parseResult;
} }
@ -226,16 +226,44 @@ GameConfig::ParseResult GameConfig::parseConfig(
pt::write_ini(destination, srcTree); pt::write_ini(destination, srcTree);
} }
} catch (pt::ini_parser_error &e) { } catch (pt::ini_parser_error &e) {
parseResult.failOutputFile(e.filename(), e.line(), e.message()); parseResult.failOutputFile(e.line(), e.message());
RW_MESSAGE(e.what()); RW_MESSAGE(e.what());
} }
return parseResult; return parseResult;
} }
std::string GameConfig::extractFilenameParseTypeData(ParseType type, const std::string &data)
{
switch (type) {
case ParseType::CONFIG:
return "<configuration>";
case ParseType::FILE:
return data;
case ParseType::STRING:
return "<string>";
case ParseType::DEFAULT:
default:
return "<default>";
}
}
GameConfig::ParseResult::ParseResult(
GameConfig::ParseType srcType, const std::string &source,
GameConfig::ParseType destType, const std::string &destination)
: m_result(ErrorType::GOOD)
, m_inputfilename(GameConfig::extractFilenameParseTypeData(srcType, source))
, m_outputfilename(GameConfig::extractFilenameParseTypeData(destType, destination))
, m_line(0)
, m_message()
, m_keys_requiredMissing()
, m_keys_invalidData() {
}
GameConfig::ParseResult::ParseResult() GameConfig::ParseResult::ParseResult()
: m_result(ErrorType::GOOD) : m_result(ErrorType::GOOD)
, m_filename() , m_inputfilename()
, m_outputfilename()
, m_line(0) , m_line(0)
, m_message() , m_message()
, m_keys_requiredMissing() , m_keys_requiredMissing()
@ -250,10 +278,9 @@ bool GameConfig::ParseResult::isValid() const {
return this->type() == ErrorType::GOOD; return this->type() == ErrorType::GOOD;
} }
void GameConfig::ParseResult::failInputFile(const std::string &filename, size_t line, void GameConfig::ParseResult::failInputFile(size_t line,
const std::string &message) { const std::string &message) {
this->m_result = ParseResult::ErrorType::INVALIDINPUTFILE; this->m_result = ParseResult::ErrorType::INVALIDINPUTFILE;
this->m_filename = filename;
this->m_line = line; this->m_line = line;
this->m_message = message; this->m_message = message;
} }
@ -272,9 +299,9 @@ void GameConfig::ParseResult::failInvalidData(const std::string &key) {
this->m_keys_invalidData.push_back(key); this->m_keys_invalidData.push_back(key);
} }
void GameConfig::ParseResult::failOutputFile(const std::string &filename, size_t line, const std::string &message) { void GameConfig::ParseResult::failOutputFile(size_t line,
const std::string &message) {
this->m_result = ParseResult::ErrorType::INVALIDOUTPUTFILE; this->m_result = ParseResult::ErrorType::INVALIDOUTPUTFILE;
this->m_filename = filename;
this->m_line = line; this->m_line = line;
this->m_message = message; this->m_message = message;
} }
@ -287,3 +314,48 @@ const std::vector<std::string> &GameConfig::ParseResult::getKeysInvalidData() co
return this->m_keys_invalidData; return this->m_keys_invalidData;
} }
std::string GameConfig::ParseResult::what() const {
switch (this->m_result) {
case ErrorType::GOOD:
return "Good";
case ErrorType::INVALIDARGUMENT:
return "Invalid argument: destination cannot be the default config";
case ErrorType::INVALIDINPUTFILE:
{
std::ostringstream oss;
oss << "Error while reading \""
<< this->m_inputfilename << "\":" << this->m_line << ":\n"
<< this->m_message;
return oss.str();
}
case ErrorType::INVALIDOUTPUTFILE:
{
std::ostringstream oss;
oss << "Error while writing \""
<< this->m_inputfilename << "\":" << this->m_line << ":\n"
<< this->m_message;
return oss.str();
}
case ErrorType::INVALIDCONTENT:
{
std::ostringstream oss;
oss << "Error while parsing \""
<< this->m_inputfilename << "\".";
if (this->m_keys_requiredMissing.size()) {
oss << "\nRequired keys that are missing:";
for (auto &key : this->m_keys_requiredMissing) {
oss << "\n - " << key;
}
}
if (this->m_keys_invalidData.size()) {
oss << "\nKeys that contain invalid data:";
for (auto &key : this->m_keys_invalidData) {
oss << "\n - " << key;
}
}
return oss.str();
}
default:
return "Unknown error";
}
}

View File

@ -4,6 +4,19 @@
#include <vector> #include <vector>
class GameConfig { class GameConfig {
private:
enum ParseType {
DEFAULT,
CONFIG,
FILE,
STRING
};
/**
* @brief extractFilenameParseTypeData Get a human readable filename string
* @return file path or a description of the data type
*/
static std::string extractFilenameParseTypeData(ParseType type, const std::string &data);
public: public:
class ParseResult { class ParseResult {
public: public:
@ -19,11 +32,23 @@ public:
/// INVALIDOUTPUTFILE: There was some error while writing to a file or string /// INVALIDOUTPUTFILE: There was some error while writing to a file or string
INVALIDOUTPUTFILE INVALIDOUTPUTFILE
}; };
private:
/** /**
* @brief ParseResult Holds the issues occurred while parsing of a config file. * @brief ParseResult Holds the issues occurred while parsing of a config file.
* @param srcType Type of the source
* @param source The source of the parser
* @param destType Type of the destination
* @param destination The destination
*/ */
ParseResult(ParseType srcType, const std::string &source,
ParseType destType, const std::string &destination);
/**
* @brief ParseResult Create empty ParseResult
*/
ParseResult(); ParseResult();
public:
/** /**
* @brief type Get the type of error * @brief type Get the type of error
* @return Type of error or GOOD if there was no error * @return Type of error or GOOD if there was no error
@ -43,14 +68,15 @@ public:
const std::vector<std::string> &getKeysInvalidData() const; const std::vector<std::string> &getKeysInvalidData() const;
/** /**
* @brief failInputFile Fail because the input file was invalid * @brief failInputFile Fail because the input file was invalid
* @param filename Filename of the invalid file
* @param line Line number where the error is located * @param line Line number where the error is located
* @param message Description of the error * @param message Description of the error
*/ */
void failInputFile(const std::string &filename, size_t line, const std::string &message); void failInputFile(size_t line, const std::string &message);
/** /**
* @brief failArgument Fail because an argument was invalid * @brief failArgument Fail because an argument was invalid
* @param srcType type of the source
* @param destType type of the destination
*/ */
void failArgument(); void failArgument();
@ -68,23 +94,31 @@ public:
/** /**
* @brief failOutputFile Fail because an error occurred while while writing to the output * @brief failOutputFile Fail because an error occurred while while writing to the output
* @param filename Filename of the invalid file
* @param line Line number where the error is located * @param line Line number where the error is located
* @param message Description of the error * @param message Description of the error
*/ */
void failOutputFile(const std::string &filename, size_t line, const std::string &message); void failOutputFile(size_t line, const std::string &message);
/** /**
* @brief isValid * @brief isValid
* @return True if the loaded configuration is valid * @return True if the loaded configuration is valid
*/ */
bool isValid() const; bool isValid() const;
/**
* @brief what Get a string representing the error
* @return String with the error description
*/
std::string what() const;
private: private:
/// Type of the failure /// Type of the failure
ErrorType m_result; ErrorType m_result;
/// Filename of the invalid input or output file /// Filename of the input file
std::string m_filename; std::string m_inputfilename;
/// Filename of the output file
std::string m_outputfilename;
/// Line number where the failure occurred (on invalid input or output file) /// Line number where the failure occurred (on invalid input or output file)
size_t m_line; size_t m_line;
@ -97,6 +131,8 @@ public:
/// All keys that contain invalid data /// All keys that contain invalid data
std::vector<std::string> m_keys_invalidData; std::vector<std::string> m_keys_invalidData;
friend class GameConfig;
}; };
/** /**
@ -148,13 +184,6 @@ public:
private: private:
static std::string getDefaultConfigPath(); static std::string getDefaultConfigPath();
enum ParseType {
DEFAULT,
CONFIG,
FILE,
STRING
};
/** /**
* @brief parseConfig Load data from source and write it to destination. * @brief parseConfig Load data from source and write it to destination.
* @param srcType Can be DEFAULT | CONFIG | FILE | STRING * @param srcType Can be DEFAULT | CONFIG | FILE | STRING

View File

@ -126,7 +126,7 @@ BOOST_AUTO_TEST_CASE(test_config_valid) {
GameConfig config(tempFile.filename(), tempFile.dirname()); GameConfig config(tempFile.filename(), tempFile.dirname());
BOOST_CHECK(config.isValid()); BOOST_CHECK(config.isValid());
BOOST_CHECK_EQUAL(config.getParseResult().type(), GameConfig::ParseResult::GOOD); BOOST_CHECK_EQUAL(config.getParseResult().type(), GameConfig::ParseResult::ErrorType::GOOD);
BOOST_CHECK_EQUAL(config.getParseResult().getKeysRequiredMissing().size(), 0); BOOST_CHECK_EQUAL(config.getParseResult().getKeysRequiredMissing().size(), 0);
BOOST_CHECK_EQUAL(config.getParseResult().getKeysInvalidData().size(), 0); BOOST_CHECK_EQUAL(config.getParseResult().getKeysInvalidData().size(), 0);
@ -147,7 +147,7 @@ BOOST_AUTO_TEST_CASE(test_config_valid_modified) {
GameConfig config(tempFile.filename(), tempFile.dirname()); GameConfig config(tempFile.filename(), tempFile.dirname());
BOOST_CHECK(config.isValid()); BOOST_CHECK(config.isValid());
BOOST_CHECK_EQUAL(config.getParseResult().type(), GameConfig::ParseResult::GOOD); BOOST_CHECK_EQUAL(config.getParseResult().type(), GameConfig::ParseResult::ErrorType::GOOD);
BOOST_CHECK_EQUAL(config.getParseResult().getKeysRequiredMissing().size(), 0); BOOST_CHECK_EQUAL(config.getParseResult().getKeysRequiredMissing().size(), 0);
BOOST_CHECK_EQUAL(config.getParseResult().getKeysInvalidData().size(), 0); BOOST_CHECK_EQUAL(config.getParseResult().getKeysInvalidData().size(), 0);
@ -191,7 +191,7 @@ BOOST_AUTO_TEST_CASE(test_config_save_readonly) {
auto writeResult = config.saveConfig(); auto writeResult = config.saveConfig();
BOOST_CHECK(!writeResult.isValid()); BOOST_CHECK(!writeResult.isValid());
BOOST_CHECK_EQUAL(writeResult.type(), GameConfig::ParseResult::INVALIDOUTPUTFILE); BOOST_CHECK_EQUAL(writeResult.type(), GameConfig::ParseResult::ErrorType::INVALIDOUTPUTFILE);
} }
BOOST_AUTO_TEST_CASE(test_config_valid_default) { BOOST_AUTO_TEST_CASE(test_config_valid_default) {
@ -221,7 +221,7 @@ BOOST_AUTO_TEST_CASE(test_config_invalid_duplicate) {
BOOST_CHECK(!config.isValid()); BOOST_CHECK(!config.isValid());
const auto &parseResult = config.getParseResult(); const auto &parseResult = config.getParseResult();
BOOST_CHECK_EQUAL(parseResult.type(), GameConfig::ParseResult::INVALIDINPUTFILE); BOOST_CHECK_EQUAL(parseResult.type(), GameConfig::ParseResult::ErrorType::INVALIDINPUTFILE);
} }
BOOST_AUTO_TEST_CASE(test_config_invalid_required_missing) { BOOST_AUTO_TEST_CASE(test_config_invalid_required_missing) {
@ -237,7 +237,7 @@ BOOST_AUTO_TEST_CASE(test_config_invalid_required_missing) {
BOOST_CHECK(!config.isValid()); BOOST_CHECK(!config.isValid());
const auto &parseResult = config.getParseResult(); const auto &parseResult = config.getParseResult();
BOOST_CHECK_EQUAL(parseResult.type(), GameConfig::ParseResult::INVALIDCONTENT); BOOST_CHECK_EQUAL(parseResult.type(), GameConfig::ParseResult::ErrorType::INVALIDCONTENT);
BOOST_CHECK_EQUAL(parseResult.getKeysRequiredMissing().size(), 1); BOOST_CHECK_EQUAL(parseResult.getKeysRequiredMissing().size(), 1);
BOOST_CHECK_EQUAL(parseResult.getKeysInvalidData().size(), 0); BOOST_CHECK_EQUAL(parseResult.getKeysInvalidData().size(), 0);
@ -258,7 +258,7 @@ BOOST_AUTO_TEST_CASE(test_config_invalid_wrong_type) {
BOOST_CHECK(!config.isValid()); BOOST_CHECK(!config.isValid());
const auto &parseResult = config.getParseResult(); const auto &parseResult = config.getParseResult();
BOOST_CHECK_EQUAL(parseResult.type(), GameConfig::ParseResult::INVALIDCONTENT); BOOST_CHECK_EQUAL(parseResult.type(), GameConfig::ParseResult::ErrorType::INVALIDCONTENT);
BOOST_CHECK_EQUAL(parseResult.getKeysRequiredMissing().size(), 0); BOOST_CHECK_EQUAL(parseResult.getKeysRequiredMissing().size(), 0);
BOOST_CHECK_EQUAL(parseResult.getKeysInvalidData().size(), 1); BOOST_CHECK_EQUAL(parseResult.getKeysInvalidData().size(), 1);
@ -278,7 +278,7 @@ BOOST_AUTO_TEST_CASE(test_config_invalid_empty) {
BOOST_CHECK(!config.isValid()); BOOST_CHECK(!config.isValid());
const auto &parseResult = config.getParseResult(); const auto &parseResult = config.getParseResult();
BOOST_CHECK_EQUAL(parseResult.type(), GameConfig::ParseResult::INVALIDCONTENT); BOOST_CHECK_EQUAL(parseResult.type(), GameConfig::ParseResult::ErrorType::INVALIDCONTENT);
BOOST_CHECK_GE(parseResult.getKeysRequiredMissing().size(), 1); BOOST_CHECK_GE(parseResult.getKeysRequiredMissing().size(), 1);
} }
@ -292,7 +292,7 @@ BOOST_AUTO_TEST_CASE(test_config_invalid_nonexisting) {
BOOST_CHECK(!config.isValid()); BOOST_CHECK(!config.isValid());
const auto &parseResult = config.getParseResult(); const auto &parseResult = config.getParseResult();
BOOST_CHECK_EQUAL(parseResult.type(), GameConfig::ParseResult::INVALIDINPUTFILE); BOOST_CHECK_EQUAL(parseResult.type(), GameConfig::ParseResult::ErrorType::INVALIDINPUTFILE);
} }
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()