Improve error codes

This commit is contained in:
2025-02-06 04:42:55 +01:00
parent 87f39d93f0
commit f465f2eb0f
5 changed files with 89 additions and 46 deletions

View File

@@ -33,18 +33,35 @@ namespace minipp
enum class EResult enum class EResult
{ {
/* Errors */ /* Errors */
KeyNotPresent = -1, KeyNotPresent = -1,
KeyAlreadyPresent = -2, KeyAlreadyPresent = -2,
SectionNotPresent = -3, SectionNotPresent = -3,
SectionAlreadyPresent = -4, SectionAlreadyPresent = -4,
FileIOError = -5, FileIOError = -5,
InvalidDataType = -6, InvalidDataType = -6,
FormatError = -7, FormatError = -7,
ArrayDataTypeInconsistency = -8, ArrayDataTypeInconsistency = -8,
BadEscapeSequence = -9,
UnknownEscapeSequence = -10,
UnescapedStringValue = -11,
ValueEmpty = -12,
IntegerValueInvalid = -13,
IntegerValueOutOfRange = -14,
IntegerStyleInvalid = -15,
FloatValueInvalid = -16,
BooleanValueInvalid = -17,
ArrayNotEnclosed = -18,
ArrayBracketsInbalanced = -19,
InvalidName = -20,
SectionExpectedClosingBracket = -21,
EmptySectionName = -22,
KeyValuePairNotInSection = -23,
ExpectedKeyValuePair = -24,
KeyEmpty = -25,
/* OK Codes */ /* OK Codes */
Success = +1, Success = +1,
ValueOverwritten = +2 ValueOverwritten = +2
}; };
enum class EIntStyle enum class EIntStyle
@@ -72,7 +89,7 @@ namespace minipp
const std::vector<std::string>& GetComments() const noexcept { return m_comments; } const std::vector<std::string>& GetComments() const noexcept { return m_comments; }
public: public:
static std::unique_ptr<Value> ParseValue(std::string value); static std::unique_ptr<Value> ParseValue(std::string value, EResult* result = nullptr);
}; };
class Values class Values
@@ -230,28 +247,32 @@ namespace minipp
EResult SetValue(const std::string& name, std::unique_ptr<ValueDataType> value, bool allowOverwrite = false) noexcept EResult SetValue(const std::string& name, std::unique_ptr<ValueDataType> value, bool allowOverwrite = false) noexcept
{ {
static_assert(std::is_base_of<Value, ValueDataType>::value, "ValueDataType must be a subclass of Value"); static_assert(std::is_base_of<Value, ValueDataType>::value, "ValueDataType must be a subclass of Value");
bool overwritten = false;
if (m_values.find(name) != m_values.end()) if (m_values.find(name) != m_values.end())
if (!allowOverwrite) if (!allowOverwrite)
return EResult::KeyAlreadyPresent; return EResult::KeyAlreadyPresent;
else else
{
delete m_values[name]; delete m_values[name];
overwritten = true;
}
m_values[name] = value.release(); m_values[name] = value.release();
return EResult::Success; return overwritten ? EResult::ValueOverwritten : EResult::Success;
} }
template<typename ValueDataType> template<typename ValueDataType>
typename ValueDataType::BaseType GetValueOrDefault(const std::string& key, typename ValueDataType::BaseType GetValueOrDefault(const std::string& key,
const typename ValueDataType::BaseType& defaultValue = typename ValueDataType::BaseType{}) const typename ValueDataType::BaseType& defaultValue = typename ValueDataType::BaseType{})
{ {
static_assert(std::is_base_of<Value, ValueDataType>::value, "ValueDataType must be a subclass of Value"); static_assert(std::is_base_of<Value, ValueDataType>::value, "ValueDataType must be a subclass of Value");
ValueDataType* value = nullptr; ValueDataType* value = nullptr;
if (GetValue(key, &value) != EResult::Success) if (GetValue(key, &value) != EResult::Success)
return defaultValue; return defaultValue;
return value->GetValue(); return value->GetValue();
} }
public: public:
EResult GetSubSection(const std::string& key, Section** destination) const noexcept; EResult GetSubSection(const std::string& key, Section** destination) const noexcept;
@@ -268,7 +289,7 @@ namespace minipp
static minipp::EResult WriteSection(const Section* section, std::ofstream& ofs, std::string partTreeName) noexcept; static minipp::EResult WriteSection(const Section* section, std::ofstream& ofs, std::string partTreeName) noexcept;
public: public:
EResult Parse(const std::string& path) noexcept; EResult Parse(const std::string& path, bool additional = false) noexcept;
EResult Write(const std::string& path) const noexcept; EResult Write(const std::string& path) const noexcept;
public: public:
@@ -312,7 +333,7 @@ namespace minipp
#define PP_COUT_SYNTAX_ERROR(line, msg) PP_COUT("Error in line " << line << ": " << msg) #define PP_COUT_SYNTAX_ERROR(line, msg) PP_COUT("Error in line " << line << ": " << msg)
std::unique_ptr<minipp::MiniPPFile::Value> minipp::MiniPPFile::Value::ParseValue(std::string value) std::unique_ptr<minipp::MiniPPFile::Value> minipp::MiniPPFile::Value::ParseValue(std::string value, EResult* result)
{ {
char valueFirstChar = value.front(); char valueFirstChar = value.front();
char valueLastChar = value.back(); char valueLastChar = value.back();
@@ -381,7 +402,7 @@ minipp::EResult minipp::MiniPPFile::Values::StringValue::Parse(const std::string
if (i + 1 >= str.size()) if (i + 1 >= str.size())
{ {
PP_COUT("Syntax error: '\\' at end of string"); PP_COUT("Syntax error: '\\' at end of string");
return EResult::FormatError; return EResult::BadEscapeSequence;
} }
switch (str[i + 1]) switch (str[i + 1])
@@ -403,12 +424,12 @@ minipp::EResult minipp::MiniPPFile::Values::StringValue::Parse(const std::string
break; break;
default: default:
PP_COUT("Syntax error: Unknown escape sequence '\\" << str[i + 1] << "'"); PP_COUT("Syntax error: Unknown escape sequence '\\" << str[i + 1] << "'");
return EResult::FormatError; return EResult::UnknownEscapeSequence;
} }
++i; ++i;
} }
else if (str[i] == '"') else if (str[i] == '"')
return EResult::FormatError; return EResult::UnescapedStringValue;
else else
m_value.push_back(str[i]); m_value.push_back(str[i]);
} }
@@ -444,6 +465,7 @@ minipp::EResult minipp::MiniPPFile::Values::StringValue::ToString(std::string& d
sanitizedValue.insert(i, 1, '\\'); sanitizedValue.insert(i, 1, '\\');
i++; i++;
break; break;
default: break;
} }
} }
@@ -480,7 +502,7 @@ minipp::EResult minipp::MiniPPFile::Values::IntValue::Parse(const std::string& s
if (!Tools::IsIntegerDecimal(sanitizedValue)) if (!Tools::IsIntegerDecimal(sanitizedValue))
{ {
PP_COUT("Invalid decimal integer value: " << sanitizedValue); PP_COUT("Invalid decimal integer value: " << sanitizedValue);
return EResult::FormatError; return EResult::IntegerValueInvalid;
} }
m_value = std::stoll(sanitizedValue); m_value = std::stoll(sanitizedValue);
m_style = EIntStyle::Decimal; m_style = EIntStyle::Decimal;
@@ -489,12 +511,12 @@ minipp::EResult minipp::MiniPPFile::Values::IntValue::Parse(const std::string& s
catch (const std::invalid_argument&) catch (const std::invalid_argument&)
{ {
PP_COUT("Invalid integer value: " << sanitizedValue); PP_COUT("Invalid integer value: " << sanitizedValue);
return EResult::FormatError; return EResult::IntegerValueInvalid;
} }
catch (const std::out_of_range&) catch (const std::out_of_range&)
{ {
PP_COUT("Integer value out of range: " << sanitizedValue); PP_COUT("Integer value out of range: " << sanitizedValue);
return EResult::FormatError; return EResult::IntegerValueOutOfRange;
} }
return EResult::Success; return EResult::Success;
@@ -534,7 +556,7 @@ minipp::EResult minipp::MiniPPFile::Values::IntValue::ToString(std::string& dest
} }
default: default:
PP_COUT("Invalid integer style."); PP_COUT("Invalid integer style.");
return EResult::FormatError; return EResult::IntegerStyleInvalid;
} }
return EResult::Success; return EResult::Success;
@@ -549,7 +571,7 @@ minipp::EResult minipp::MiniPPFile::Values::BooleanValue::Parse(const std::strin
else else
{ {
PP_COUT("Invalid boolean value: " << str << " (may only contain lowercase true and false)"); PP_COUT("Invalid boolean value: " << str << " (may only contain lowercase true and false)");
return EResult::FormatError; return EResult::BooleanValueInvalid;
} }
return EResult::Success; return EResult::Success;
} }
@@ -562,7 +584,16 @@ minipp::EResult minipp::MiniPPFile::Values::BooleanValue::ToString(std::string&
minipp::EResult minipp::MiniPPFile::Values::FloatValue::Parse(const std::string& str) noexcept minipp::EResult minipp::MiniPPFile::Values::FloatValue::Parse(const std::string& str) noexcept
{ {
m_value = std::stod(str); try
{
m_value = std::stod(str);
}
catch (...)
{
PP_COUT("Invalid float value: " << str);
return EResult::FloatValueInvalid;
}
return EResult::Success; return EResult::Success;
} }
@@ -603,7 +634,7 @@ minipp::EResult minipp::MiniPPFile::Values::ArrayValue::Parse(const std::string&
if (i + 1 >= str.size()) if (i + 1 >= str.size())
{ {
PP_COUT("Syntax error: Bad escape sequence: '\\' at end of string"); PP_COUT("Syntax error: Bad escape sequence: '\\' at end of string");
return EResult::FormatError; return EResult::BadEscapeSequence;
} }
currentElement += c; currentElement += c;
currentElement += str[i + 1]; currentElement += str[i + 1];
@@ -637,7 +668,7 @@ minipp::EResult minipp::MiniPPFile::Values::ArrayValue::Parse(const std::string&
if (bracketCounter < 0) if (bracketCounter < 0)
{ {
PP_COUT("Array brackets are not balanced. (One ] too much or encountered too early)"); PP_COUT("Array brackets are not balanced. (One ] too much or encountered too early)");
return EResult::FormatError; return EResult::ArrayBracketsInbalanced;
} }
else if (bracketCounter >= 1) else if (bracketCounter >= 1)
currentElement += c; currentElement += c;
@@ -655,7 +686,7 @@ minipp::EResult minipp::MiniPPFile::Values::ArrayValue::Parse(const std::string&
if (bracketCounter != 0) // will always be a positive value because negative values are caught earlier if (bracketCounter != 0) // will always be a positive value because negative values are caught earlier
{ {
PP_COUT("Array brackets are not balanced. (Missing " << bracketCounter << " closing brackets)"); PP_COUT("Array brackets are not balanced. (Missing " << bracketCounter << " closing brackets)");
return EResult::FormatError; return EResult::ArrayBracketsInbalanced;
} }
if (!currentElement.empty()) if (!currentElement.empty())
@@ -663,11 +694,12 @@ minipp::EResult minipp::MiniPPFile::Values::ArrayValue::Parse(const std::string&
size_t lastTypeIdHash = 0; size_t lastTypeIdHash = 0;
bool hasTypeHash = false; bool hasTypeHash = false;
EResult result;
for (auto& elem : elements) for (auto& elem : elements)
{ {
auto parsed = ParseValue(elem); auto parsed = ParseValue(elem, &result);
if (parsed == nullptr) if (parsed == nullptr)
return EResult::FormatError; return result;
if (!hasTypeHash) if (!hasTypeHash)
{ {
@@ -774,7 +806,7 @@ minipp::EResult minipp::MiniPPFile::WriteSection(const Section* section, std::of
if (!Tools::IsNameValid(pair.first)) if (!Tools::IsNameValid(pair.first))
{ {
PP_COUT("Invalid name for key: " << pair.first); PP_COUT("Invalid name for key: " << pair.first);
return EResult::FormatError; return EResult::InvalidName;
} }
for (const auto& comment : pair.second->m_comments) for (const auto& comment : pair.second->m_comments)
@@ -797,7 +829,7 @@ minipp::EResult minipp::MiniPPFile::WriteSection(const Section* section, std::of
if (!Tools::IsNameValid(pair.first)) if (!Tools::IsNameValid(pair.first))
{ {
PP_COUT("Invalid name for section: " << pair.first); PP_COUT("Invalid name for section: " << pair.first);
return EResult::FormatError; return EResult::InvalidName;
} }
for (const auto& comment : pair.second->m_comments) for (const auto& comment : pair.second->m_comments)
@@ -812,8 +844,15 @@ minipp::EResult minipp::MiniPPFile::WriteSection(const Section* section, std::of
return EResult::Success; return EResult::Success;
} }
minipp::EResult minipp::MiniPPFile::Parse(const std::string& path) noexcept minipp::EResult minipp::MiniPPFile::Parse(const std::string& path, bool additional) noexcept
{ {
if (!additional)
{
m_rootSection.m_comments.clear();
m_rootSection.m_values.clear();
m_rootSection.m_subSections.clear();
}
std::ifstream ifs; std::ifstream ifs;
ifs.open(path); ifs.open(path);
if (!ifs.is_open()) if (!ifs.is_open())
@@ -845,14 +884,14 @@ minipp::EResult minipp::MiniPPFile::Parse(const std::string& path) noexcept
if (lastChar != ']') if (lastChar != ']')
{ {
PP_COUT_SYNTAX_ERROR(lineCounter, "Expected ']' at the end of the line."); PP_COUT_SYNTAX_ERROR(lineCounter, "Expected ']' at the end of the line.");
return EResult::FormatError; return EResult::SectionExpectedClosingBracket;
} }
std::string sectionPathStr = currentLine.substr(1, currentLine.size() - 2); std::string sectionPathStr = currentLine.substr(1, currentLine.size() - 2);
Tools::StringTrim(sectionPathStr); Tools::StringTrim(sectionPathStr);
if (sectionPathStr.empty()) if (sectionPathStr.empty())
{ {
PP_COUT_SYNTAX_ERROR(lineCounter, "Expected section path. Found empty section begin notation."); PP_COUT_SYNTAX_ERROR(lineCounter, "Expected section path. Found empty section begin notation.");
return EResult::FormatError; return EResult::EmptySectionName;
} }
// Create section tree // Create section tree
Section* ubSection = &m_rootSection; Section* ubSection = &m_rootSection;
@@ -865,14 +904,14 @@ minipp::EResult minipp::MiniPPFile::Parse(const std::string& path) noexcept
if (!Tools::IsNameValid(sectionName)) if (!Tools::IsNameValid(sectionName))
{ {
PP_COUT_SYNTAX_ERROR(lineCounter, "Invalid section name. (\"" << sectionName << "\") May only contain [a - z][A - Z][0 - 9] and _."); PP_COUT_SYNTAX_ERROR(lineCounter, "Invalid section name. (\"" << sectionName << "\") May only contain [a - z][A - Z][0 - 9] and _.");
return EResult::FormatError; return EResult::InvalidName;
} }
result = ubSection->SetSubSection(sectionName, std::make_unique<Section>(), false); result = ubSection->SetSubSection(sectionName, std::make_unique<Section>(), false);
if (result == EResult::SectionAlreadyPresent && i == sectionPath.size() - 1) if (result == EResult::SectionAlreadyPresent && i == sectionPath.size() - 1)
{ {
PP_COUT_SYNTAX_ERROR(lineCounter, "All (sub-) sections may only be defined once."); PP_COUT_SYNTAX_ERROR(lineCounter, "All (sub-) sections may only be defined once.");
return EResult::FormatError; return EResult::SectionAlreadyPresent;
} }
ubSection->GetSubSection(sectionName, &ubSection); ubSection->GetSubSection(sectionName, &ubSection);
} }
@@ -884,14 +923,14 @@ minipp::EResult minipp::MiniPPFile::Parse(const std::string& path) noexcept
if (currentSection == nullptr) if (currentSection == nullptr)
{ {
PP_COUT_SYNTAX_ERROR(lineCounter, "Expected section begin before key-value pair."); PP_COUT_SYNTAX_ERROR(lineCounter, "Expected section begin before key-value pair.");
return EResult::FormatError; return EResult::KeyValuePairNotInSection;
} }
int64_t keyValueDelimiterIndex = Tools::FirstIndexOf(currentLine, '='); int64_t keyValueDelimiterIndex = Tools::FirstIndexOf(currentLine, '=');
if (keyValueDelimiterIndex == -1) if (keyValueDelimiterIndex == -1)
{ {
PP_COUT_SYNTAX_ERROR(lineCounter, "Expected '=' in line."); PP_COUT_SYNTAX_ERROR(lineCounter, "Expected '=' in line.");
return EResult::FormatError; return EResult::ExpectedKeyValuePair;
} }
auto keyValuePair = Tools::SplitInTwo(currentLine, keyValueDelimiterIndex); auto keyValuePair = Tools::SplitInTwo(currentLine, keyValueDelimiterIndex);
@@ -901,24 +940,24 @@ minipp::EResult minipp::MiniPPFile::Parse(const std::string& path) noexcept
if (keyValuePair.first.empty()) if (keyValuePair.first.empty())
{ {
PP_COUT_SYNTAX_ERROR(lineCounter, "Expected key in line."); PP_COUT_SYNTAX_ERROR(lineCounter, "Expected key in line.");
return EResult::FormatError; return EResult::KeyEmpty;
} }
if (!Tools::IsNameValid(keyValuePair.first)) if (!Tools::IsNameValid(keyValuePair.first))
{ {
PP_COUT_SYNTAX_ERROR(lineCounter, "Invalid key name. (\"" << keyValuePair.first << "\") May only contain [a - z][A - Z][0 - 9] and _."); PP_COUT_SYNTAX_ERROR(lineCounter, "Invalid key name. (\"" << keyValuePair.first << "\") May only contain [a - z][A - Z][0 - 9] and _.");
return EResult::FormatError; return EResult::InvalidName;
} }
if (keyValuePair.second.empty()) if (keyValuePair.second.empty())
{ {
PP_COUT_SYNTAX_ERROR(lineCounter, "Empty keys are not allowed"); PP_COUT_SYNTAX_ERROR(lineCounter, "Empty keys are not allowed");
return EResult::FormatError; return EResult::ValueEmpty;
} }
auto parsedValue = Value::ParseValue(keyValuePair.second); auto parsedValue = Value::ParseValue(keyValuePair.second);
if (parsedValue == nullptr) if (parsedValue == nullptr)
{ {
PP_COUT_SYNTAX_ERROR(lineCounter, "Invalid value"); PP_COUT_SYNTAX_ERROR(lineCounter, "Invalid value");
return EResult::FormatError; return EResult::InvalidName;
} }
parsedValue->m_comments = commentBuffer; parsedValue->m_comments = commentBuffer;
commentBuffer.clear(); commentBuffer.clear();

2
minipp/simple.mini Normal file
View File

@@ -0,0 +1,2 @@
[game]
what=["test\t\\yeah", "hai"]

View File

@@ -8,7 +8,7 @@ int main()
EResult result; EResult result;
MiniPPFile file; MiniPPFile file;
result = file.Parse("test.mini"); result = file.Parse("simple.mini");
auto& root = file.GetRoot(); auto& root = file.GetRoot();

View File

@@ -7,6 +7,7 @@ completionPercentage = 50.0f
is_completed = false is_completed = false
testargs = ["this is a \\\"test\"", "this is\n the next line"] testargs = ["this is a \\\"test\"", "this is\n the next line"]
testTestArg = [["yeah", "new\nline"], ["hallo\ttest\n\\\\"]] testTestArg = [["yeah", "new\nline"], ["hallo\ttest\n\\\\"]]
testEmpty = []
# This section is about # This section is about
# the settings of a game window # the settings of a game window

View File

@@ -1,4 +1,5 @@
[game] [game]
testEmpty = []
name = "Test Game\nNext Line" name = "Test Game\nNext Line"
testTestArg = [["yeah", "new\nline"], ["hallo\ttest\n\\\\"]] testTestArg = [["yeah", "new\nline"], ["hallo\ttest\n\\\\"]]
completionPercentage = 50.000000 completionPercentage = 50.000000