Skip to content

Commit

Permalink
Simplify txtdata parsing with a helper class
Browse files Browse the repository at this point in the history
  • Loading branch information
glebm committed Nov 12, 2023
1 parent bafac8e commit 9459712
Show file tree
Hide file tree
Showing 7 changed files with 267 additions and 642 deletions.
3 changes: 2 additions & 1 deletion Source/.clang-tidy
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ Checks: >
-modernize-avoid-c-arrays,
-modernize-use-trailing-return-type,
-modernize-concat-nested-namespaces,
-modernize-avoid-bind
-modernize-avoid-bind,
-modernize-use-constraints
HeaderFilterRegex: "^(Source|test)\\.h$"

Expand Down
1 change: 1 addition & 0 deletions Source/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ set(libdevilutionx_SRCS

data/file.cpp
data/parser.cpp
data/record_reader.cpp

DiabloUI/button.cpp
DiabloUI/credits.cpp
Expand Down
4 changes: 2 additions & 2 deletions Source/data/iterators.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ class DataFileField {
}

template <typename T, typename ParseFn>
[[nodiscard]] tl::expected<void, Error> parseEnumList(T &destination, ParseFn &&parseFn)
[[nodiscard]] tl::expected<void, std::string> parseEnumList(T &destination, ParseFn &&parseFn)
{
destination = {};
const std::string_view str = value();
Expand All @@ -155,7 +155,7 @@ class DataFileField {
for (const std::string_view part : SplitByChar(str, ',')) {
auto result = parseFn(part);
if (!result.has_value())
return tl::make_unexpected(Error::InvalidValue);
return tl::make_unexpected(std::move(result).error());
destination |= result.value();
}
return {};
Expand Down
17 changes: 17 additions & 0 deletions Source/data/record_reader.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#include "data/record_reader.hpp"

namespace devilution {

void RecordReader::advance()
{
if (needsIncrement_) {
++it_;
} else {
needsIncrement_ = true;
}
if (it_ == end_) {
DataFile::reportFatalError(DataFile::Error::NotEnoughColumns, filename_);
}
}

} // namespace devilution
123 changes: 123 additions & 0 deletions Source/data/record_reader.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
#pragma once

#include <string_view>
#include <type_traits>

#include <expected.hpp>
#include <function_ref.hpp>

#include "data/file.hpp"
#include "data/iterators.hpp"

namespace devilution {

/**
* @brief A record reader that treats every error as fatal.
*/
class RecordReader {
public:
RecordReader(DataFileRecord &record, std::string_view filename)
: it_(record.begin())
, end_(record.end())
, filename_(filename)
{
}

template <typename T>
typename std::enable_if_t<std::is_integral_v<T>, void>
readInt(std::string_view name, T &out)
{
advance();
DataFileField field = *it_;
if (tl::expected<void, DataFileField::Error> result = field.parseInt(out); !result.has_value()) {
DataFile::reportFatalFieldError(result.error(), filename_, name, field);
}
}

template <typename T>
typename std::enable_if_t<std::is_integral_v<T>, void>
readOptionalInt(std::string_view name, T &out)
{
advance();
DataFileField field = *it_;
if (field.value().empty()) return;
if (tl::expected<void, DataFileField::Error> result = field.parseInt(out); !result.has_value()) {
DataFile::reportFatalFieldError(result.error(), filename_, name, field);
}
}

template <typename T, size_t N>
void readIntArray(std::string_view name, T (&out)[N])
{
advance();
DataFileField field = *it_;
if (tl::expected<void, DataFileField::Error> result = field.parseIntArray(out); !result.has_value()) {
DataFile::reportFatalFieldError(result.error(), filename_, name, field);
}
}

template <typename T>
typename std::enable_if_t<std::is_integral_v<T>, void>
readFixed6(std::string_view name, T &out)
{
advance();
DataFileField field = *it_;
if (tl::expected<void, DataFileField::Error> result = field.parseFixed6(out); !result.has_value()) {
DataFile::reportFatalFieldError(result.error(), filename_, name, field);
}
}

void readBool(std::string_view name, bool &out)
{
advance();
DataFileField field = *it_;
if (tl::expected<void, DataFileField::Error> result = field.parseBool(out); !result.has_value()) {
DataFile::reportFatalFieldError(result.error(), filename_, name, field);
}
}

void readString(std::string_view name, std::string &out)
{
advance();
out = (*it_).value();
}

template <typename T, typename F>
void read(std::string_view name, T &out, F &&parseFn)
{
advance();
DataFileField field = *it_;
if (tl::expected<T, std::string> result = parseFn(field.value()); result.has_value()) {
out = *std::move(result);
} else {
DataFile::reportFatalFieldError(DataFileField::Error::InvalidValue, filename_, name, field, result.error());
}
}

template <typename T, typename F>
void readEnumList(std::string_view name, T &out, F &&parseFn)
{
advance();
DataFileField field = *it_;
if (tl::expected<void, std::string> result = field.parseEnumList(out, std::forward<F>(parseFn)); !result.has_value()) {
DataFile::reportFatalFieldError(DataFileField::Error::InvalidValue, filename_, name, field, result.error());
}
}

std::string_view value()
{
advance();
needsIncrement_ = false;
return (*it_).value();
}

void advance();

private:
FieldIterator it_;
const FieldIterator end_;
std::string_view filename_;
bool needsIncrement_ = false;
};

} // namespace devilution
Loading

0 comments on commit 9459712

Please sign in to comment.