12#include "misc/Error.h"
14#include "misc/ProjectPaths.h"
15#include "io/ConfigManager.h"
16#include "io/properties/GuidProperty.h"
17#include "io/properties/SerializeProperty.h"
18#include "io/serializers/SerializableObjectFieldSerializer.h"
19#include "io/FieldSerializerManager.h"
22#include "Refureku/Object.h"
25#include "Serializable.generated.h"
27namespace ne RNAMESPACE() {
74 template <
typename SmartPo
inter,
typename InnerType =
typename SmartPo
inter::element_type>
75 requires std::same_as<SmartPointer, sgc::GcPtr<Serializable>> ||
76 std::same_as<SmartPointer, std::unique_ptr<Serializable>>
92 this->pObject = std::move(
pObject);
113 class RCLASS(
Guid(
"f5a59b47-ead8-4da4-892e-cf05abb2f3cc"))
Serializable :
public rfk::Object {
130 static std::variant<std::pair<std::set<std::string>, toml::value>,
Error>
131 getIdsFromFile(std::filesystem::path pathToFile);
179 [[nodiscard]] std::optional<Error> serialize(
180 std::filesystem::path pathToFile,
182 const std::unordered_map<std::string, std::string>& customAttributes = {});
203 [[nodiscard]]
static std::optional<Error> serializeMultiple(
204 std::filesystem::path pathToFile,
205 std::vector<SerializableObjectInformation> vObjects,
228 [[nodiscard]] std::variant<std::string, Error> serialize(
229 toml::value& tomlData,
230 const std::string& sEntityId =
"",
231 const std::unordered_map<std::string, std::string>& customAttributes = {},
232 const std::optional<std::filesystem::path>& optionalPathToFile = {},
233 bool bEnableBackup =
false);
259 [[nodiscard]] std::variant<std::string, Error> serialize(
260 toml::value& tomlData,
262 std::string sEntityId =
"",
263 const std::unordered_map<std::string, std::string>& customAttributes = {},
264 const std::optional<std::filesystem::path>& optionalPathToFile = {},
265 bool bEnableBackup =
false);
280 template <
typename SmartPo
inter,
typename InnerType =
typename SmartPo
inter::element_type>
281 requires std::derived_from<InnerType, Serializable> &&
282 (std::same_as<SmartPointer, sgc::GcPtr<InnerType>> ||
283 std::same_as<SmartPointer, std::unique_ptr<InnerType>>)
284 static std::variant<SmartPointer, Error> deserialize(
const std::filesystem::path& pathToFile);
299 template <
typename SmartPo
inter,
typename InnerType =
typename SmartPo
inter::element_type>
300 requires std::derived_from<InnerType, Serializable> &&
301 (std::same_as<SmartPointer, sgc::GcPtr<InnerType>> ||
302 std::same_as<SmartPointer, std::unique_ptr<InnerType>>)
303 static std::variant<SmartPointer, Error> deserialize(
304 const std::filesystem::path& pathToFile,
305 std::unordered_map<std::string, std::string>& customAttributes);
322 template <
typename SmartPo
inter,
typename InnerType =
typename SmartPo
inter::element_type>
323 requires std::derived_from<InnerType, Serializable> &&
324 (std::same_as<SmartPointer, sgc::GcPtr<InnerType>> ||
325 std::same_as<SmartPointer, std::unique_ptr<InnerType>>)
326 static std::variant<SmartPointer, Error> deserialize(
327 std::filesystem::path pathToFile,
328 std::unordered_map<std::string, std::string>& customAttributes,
329 const std::string& sEntityId);
345 template <
typename SmartPo
inter,
typename InnerType =
typename SmartPo
inter::element_type>
346 requires std::derived_from<InnerType, Serializable> &&
347 (std::same_as<SmartPointer, sgc::GcPtr<InnerType>> ||
348 std::same_as<SmartPointer, std::unique_ptr<InnerType>>)
349 static std::variant<SmartPointer, Error> deserialize(
350 const std::filesystem::path& pathToFile,
const std::string& sEntityId);
360 template <
typename SmartPo
inter,
typename InnerType =
typename SmartPo
inter::element_type>
361 requires std::same_as<SmartPointer, sgc::GcPtr<Serializable>> ||
362 std::same_as<SmartPointer, std::unique_ptr<Serializable>>
363 static std::variant<std::vector<DeserializedObjectInformation<SmartPointer>>,
Error>
364 deserializeMultiple(std::filesystem::path pathToFile);
386 template <
typename SmartPo
inter,
typename InnerType =
typename SmartPo
inter::element_type>
387 requires std::derived_from<InnerType, Serializable> &&
388 (std::same_as<SmartPointer, sgc::GcPtr<InnerType>> ||
389 std::same_as<SmartPointer, std::unique_ptr<InnerType>>)
390 static std::variant<SmartPointer, Error> deserialize(
391 const toml::value& tomlData,
392 std::unordered_map<std::string, std::string>& customAttributes,
393 std::string sEntityId =
"",
394 const std::optional<std::filesystem::path>& optionalPathToFile = {});
411 std::optional<std::pair<std::string, std::string>> getPathDeserializedFromRelativeToRes()
const;
433 [[nodiscard]]
static std::optional<Error> resolvePathToToml(std::filesystem::path& pathToFile);
442 static const rfk::Class* getClassForGuid(
const std::string& sGuid);
453 static const rfk::Struct*
454 getClassForGuid(
const rfk::Struct* pArchetypeToAnalyze,
const std::string& sGuid);
473 template <
typename SmartPo
inter,
typename InnerType =
typename SmartPo
inter::element_type>
474 requires std::derived_from<InnerType, Serializable> &&
475 (std::same_as<SmartPointer, sgc::GcPtr<InnerType>> ||
476 std::same_as<SmartPointer, std::unique_ptr<InnerType>>)
477 static std::variant<SmartPointer, Error> deserializeFromSection(
478 const toml::value& tomlData,
479 std::unordered_map<std::string, std::string>& customAttributes,
480 const std::string& sSectionName,
481 const std::string& sTypeGuid,
482 const std::string& sEntityId,
483 const std::optional<std::filesystem::path>& optionalPathToFile);
500 static inline const auto sSubEntityFieldNameKey =
".field_name";
506 static inline const auto sPathRelativeToResKey =
".path_relative_to_res";
509 static inline const auto sNothingToSerializeKey =
".none";
512 static constexpr std::string_view sCustomAttributePrefix =
"..";
514 ne_Serializable_GENERATED
517 template <
typename SmartPo
inter,
typename InnerType>
518 requires std::derived_from<InnerType, Serializable> &&
519 (std::same_as<SmartPointer, sgc::GcPtr<InnerType>> ||
520 std::same_as<SmartPointer, std::unique_ptr<InnerType>>)
522 std::unordered_map<std::string, std::string> foundCustomAttributes;
523 return deserialize<SmartPointer>(pathToFile, foundCustomAttributes);
526 template <
typename SmartPo
inter,
typename InnerType>
527 requires std::derived_from<InnerType, Serializable> &&
528 (std::same_as<SmartPointer, sgc::GcPtr<InnerType>> ||
529 std::same_as<SmartPointer, std::unique_ptr<InnerType>>)
531 const std::filesystem::path& pathToFile,
532 std::unordered_map<std::string, std::string>& customAttributes) {
533 return deserialize<SmartPointer>(pathToFile, customAttributes,
"");
536 template <
typename SmartPo
inter,
typename InnerType>
537 requires std::derived_from<InnerType, Serializable> &&
538 (std::same_as<SmartPointer, sgc::GcPtr<InnerType>> ||
539 std::same_as<SmartPointer, std::unique_ptr<InnerType>>)
541 const std::filesystem::path& pathToFile,
const std::string& sEntityId) {
542 std::unordered_map<std::string, std::string> foundCustomAttributes;
543 return deserialize<SmartPointer>(pathToFile, foundCustomAttributes, sEntityId);
546 template <
typename SmartPo
inter,
typename InnerType>
547 requires std::same_as<SmartPointer, sgc::GcPtr<Serializable>> ||
548 std::same_as<SmartPointer, std::unique_ptr<Serializable>>
549 std::variant<std::vector<DeserializedObjectInformation<SmartPointer>>,
Error>
553 if (optionalError.has_value()) [[unlikely]] {
554 auto error = std::move(optionalError.value());
555 error.addCurrentLocationToErrorStack();
560 toml::value tomlData;
562 tomlData = toml::parse(pathToFile);
563 }
catch (std::exception& exception) {
564 return Error(std::format(
565 "failed to parse TOML file at \"{}\", error: {}", pathToFile.string(), exception.what()));
569 const auto fileTable = tomlData.as_table();
570 if (fileTable.empty()) [[unlikely]] {
571 return Error(
"provided toml value has 0 sections while expected at least 1 section");
575 std::vector<DeserializedObjectInformation<SmartPointer>> deserializedObjects;
576 for (
const auto& [sSectionName, tomlValue] : fileTable) {
578 const auto iIdEndDotPos = sSectionName.rfind(
'.');
579 if (iIdEndDotPos == std::string::npos) [[unlikely]] {
580 return Error(
"provided toml value does not contain entity ID");
582 const auto sTypeGuid = sSectionName.substr(iIdEndDotPos + 1);
585 const auto sEntityId = sSectionName.substr(0, iIdEndDotPos);
588 if (sEntityId.contains(
'.')) {
595 std::unordered_map<std::string, std::string> customAttributes;
596 auto result = deserializeFromSection<SmartPointer>(
597 tomlData, customAttributes, sSectionName, sTypeGuid, sEntityId, pathToFile);
598 if (std::holds_alternative<Error>(result)) [[unlikely]] {
599 auto error = std::get<Error>(std::move(result));
600 error.addCurrentLocationToErrorStack();
606 std::get<SmartPointer>(std::move(result)), sEntityId, customAttributes);
607 deserializedObjects.push_back(std::move(objectInfo));
610 return deserializedObjects;
613 template <
typename SmartPo
inter,
typename InnerType>
614 requires std::derived_from<InnerType, Serializable> &&
615 (std::same_as<SmartPointer, sgc::GcPtr<InnerType>> ||
616 std::same_as<SmartPointer, std::unique_ptr<InnerType>>)
618 std::filesystem::path pathToFile,
619 std::unordered_map<std::string, std::string>& customAttributes,
620 const std::string& sEntityId) {
622 auto optionalError = resolvePathToToml(pathToFile);
623 if (optionalError.has_value()) [[unlikely]] {
624 auto error = std::move(optionalError.value());
625 error.addCurrentLocationToErrorStack();
630 toml::value tomlData;
632 tomlData = toml::parse(pathToFile);
633 }
catch (std::exception& exception) {
634 return Error(std::format(
635 "failed to parse TOML file at \"{}\", error: {}", pathToFile.string(), exception.what()));
639 return deserialize<SmartPointer>(tomlData, customAttributes, sEntityId, pathToFile);
642 template <
typename SmartPo
inter,
typename InnerType>
643 requires std::derived_from<InnerType, Serializable> &&
644 (std::same_as<SmartPointer, sgc::GcPtr<InnerType>> ||
645 std::same_as<SmartPointer, std::unique_ptr<InnerType>>)
647 const toml::value& tomlData,
648 std::unordered_map<std::string, std::string>& customAttributes,
649 std::string sEntityId,
650 const std::optional<std::filesystem::path>& optionalPathToFile) {
651 if (sEntityId.empty()) {
657 const auto fileTable = tomlData.as_table();
658 if (fileTable.empty()) [[unlikely]] {
659 return Error(
"provided toml value has 0 sections while expected at least 1 section");
665 std::string sTargetSection;
666 std::string sTypeGuid;
667 for (
const auto& [sSectionName, value] : fileTable) {
674 const auto iIdEndDotPos = sSectionName.rfind(
'.');
675 if (iIdEndDotPos == std::string::npos) [[unlikely]] {
676 return Error(std::format(
"section name \"{}\" does not contain entity ID", sSectionName));
678 if (iIdEndDotPos + 1 == sSectionName.size()) [[unlikely]] {
679 return Error(std::format(
"section name \"{}\" does not have a GUID", sSectionName));
681 if (iIdEndDotPos == 0) [[unlikely]] {
682 return Error(std::format(
"section \"{}\" is not full", sSectionName));
686 if (std::string_view(sSectionName.data(), iIdEndDotPos) != sEntityId) {
691 sTargetSection = sSectionName;
694 sTypeGuid = sSectionName.substr(iIdEndDotPos + 1);
700 if (sTargetSection.empty()) [[unlikely]] {
701 return Error(std::format(
"could not find entity with ID \"{}\"", sEntityId));
704 return deserializeFromSection<SmartPointer>(
705 tomlData, customAttributes, sTargetSection, sTypeGuid, sEntityId, optionalPathToFile);
708 template <
typename SmartPo
inter,
typename InnerType>
709 requires std::derived_from<InnerType, Serializable> &&
710 (std::same_as<SmartPointer, sgc::GcPtr<InnerType>> ||
711 std::same_as<SmartPointer, std::unique_ptr<InnerType>>)
713 const toml::value& tomlData,
714 std::unordered_map<std::string, std::string>& customAttributes,
715 const std::string& sSectionName,
716 const std::string& sTypeGuid,
717 const std::string& sEntityId,
718 const std::optional<std::filesystem::path>& optionalPathToFile) {
720 const auto& targetSection = tomlData.at(sSectionName);
721 if (!targetSection.is_table()) [[unlikely]] {
722 return Error(std::format(
"found \"{}\" section is not a section", sSectionName));
726 const auto& sectionTable = targetSection.as_table();
727 std::unordered_map<std::string_view, const toml::value*> fieldsToDeserialize;
728 SmartPointer pOriginalEntity =
nullptr;
729 for (
const auto& [sKey, value] : sectionTable) {
730 if (sKey == sNothingToSerializeKey) {
732 }
else if (sKey == sPathRelativeToResKey) {
734 if (!value.is_string()) [[unlikely]] {
736 std::format(
"found \"{}\" key's value is not string", sPathRelativeToResKey));
740 auto deserializeResult = Serializable::deserialize<SmartPointer>(
742 if (std::holds_alternative<Error>(deserializeResult)) [[unlikely]] {
743 auto error = std::get<Error>(std::move(deserializeResult));
744 error.addCurrentLocationToErrorStack();
747 pOriginalEntity = std::get<SmartPointer>(std::move(deserializeResult));
748 }
else if (sKey.starts_with(sCustomAttributePrefix)) {
751 if (!value.is_string()) [[unlikely]] {
752 return Error(std::format(
"found custom attribute \"{}\" is not a string", sKey));
756 customAttributes[sKey.substr(sCustomAttributePrefix.size())] = value.as_string();
758 fieldsToDeserialize[sKey] = &value;
763 const auto pType = getClassForGuid(sTypeGuid);
764 if (pType ==
nullptr) [[unlikely]] {
765 if (pOriginalEntity) {
766 return Error(std::format(
767 "GUID \"{}\" was not found in the database, but "
768 "the original object at \"{}\" (ID \"{}\") was deserialized",
770 pOriginalEntity->getPathDeserializedFromRelativeToRes().value().first,
771 pOriginalEntity->getPathDeserializedFromRelativeToRes().value().second));
773 return Error(std::format(
"no type found for GUID \"{}\"", sTypeGuid));
779 if (pOriginalEntity) {
780 return Error(std::format(
781 "deserialized type for \"{}\" does not derive from {}, but "
782 "the original object at \"{}\" (ID \"{}\") was deserialized",
784 staticGetArchetype().getName(),
785 pOriginalEntity->getPathDeserializedFromRelativeToRes().value().first,
786 pOriginalEntity->getPathDeserializedFromRelativeToRes().value().second));
788 return Error(std::format(
789 "deserialized type with GUID \"{}\" does not derive from {}",
791 staticGetArchetype().getName()));
796 SmartPointer pSmartPointerInstance =
nullptr;
797 if (pOriginalEntity !=
nullptr) {
799 pSmartPointerInstance = std::move(pOriginalEntity);
801 if (pSmartPointerInstance ==
nullptr) {
802 if constexpr (std::is_same_v<SmartPointer, std::unique_ptr<InnerType>>) {
804 pSmartPointerInstance = pType->makeUniqueInstance<InnerType>();
805 if (pSmartPointerInstance ==
nullptr) [[unlikely]] {
806 return Error(std::format(
807 "unable to make an object of type \"{}\" using type's default constructor "
808 "(does type \"{}\" has a default constructor?)",
816 else if (std::is_same_v<SmartPointer, sgc::GcPtr<InnerType>>) {
820 std::unique_ptr<InnerType> pInstance = pType->makeUniqueInstance<InnerType>();
821 if (pInstance ==
nullptr) [[unlikely]] {
822 return Error(std::format(
823 "unable to make an object of type \"{}\" using type's default constructor "
824 "(does type \"{}\" has a default constructor?)",
828 sgc::GcPtr<rfk::Object> pParentGcInstance = pInstance->makeGcFromThisType();
829 pSmartPointerInstance =
dynamic_cast<InnerType*
>(pParentGcInstance.get());
830 if (pSmartPointerInstance ==
nullptr) [[unlikely]] {
831 return Error(std::format(
832 "dynamic cast failed to cast the type \"{}\" to the specified template argument "
833 "(are you trying to deserialize into a wrong type?)",
834 pParentGcInstance->getArchetype().getName()));
836 }
else [[unlikely]] {
837 return Error(
"unexpected smart pointer type received");
846 for (
auto& [sFieldName, pFieldTomlValue] : fieldsToDeserialize) {
847 if (sFieldName == sSubEntityFieldNameKey) {
854 rfk::Field
const* pField =
855 pType->getFieldByName(sFieldName.data(), rfk::EFieldFlags::Default,
true);
856 if (pField ==
nullptr) [[unlikely]] {
858 "field name \"{}\" exists in the specified toml value but does not exist in the "
859 "actual object (if you removed/renamed this reflected field from your "
860 "class/struct - ignore this warning)",
864 const auto sFieldCanonicalTypeName = std::string(pField->getCanonicalTypeName());
872 const auto pSerializeProperty = pField->getProperty<
Serialize>();
873 if (pSerializeProperty->getSerializationType() == FieldSerializationType::FST_AS_EXTERNAL_FILE ||
874 pSerializeProperty->getSerializationType() ==
875 FieldSerializationType::FST_AS_EXTERNAL_BINARY_FILE) {
878 pField->getType().getArchetype())) [[unlikely]] {
880 auto error =
Error(
"only fields of type derived from `Serializable` can use "
881 "`SerializeAsExternal` property");
883 throw std::runtime_error(error.getFullErrorMessage());
887 if (!optionalPathToFile.has_value()) [[unlikely]] {
888 return Error(
"unable to deserialize field marked as `SerializeAsExternal` "
889 "because path to the main file was not specified");
893 if (!pFieldTomlValue->is_string()) [[unlikely]] {
894 return Error(std::format(
895 "expected field \"{}\" to store external filename in file \"{}\"",
897 optionalPathToFile.value().string()));
899 const auto sExternalFileName = pFieldTomlValue->as_string();
900 const auto pathToExternalFile = optionalPathToFile.value().parent_path() / sExternalFileName;
903 const auto pFieldObject =
904 reinterpret_cast<Serializable*
>(pField->getPtrUnsafe(pSmartPointerInstance.get()));
906 if (pSerializeProperty->getSerializationType() == FST_AS_EXTERNAL_FILE) {
908 auto result = deserialize<std::unique_ptr<Serializable>>(pathToExternalFile);
909 if (std::holds_alternative<Error>(result)) [[unlikely]] {
910 auto error = std::get<Error>(std::move(result));
911 error.addCurrentLocationToErrorStack();
914 const auto pDeserializedExternalField =
915 std::get<std::unique_ptr<Serializable>>(std::move(result));
919 pDeserializedExternalField.get(), pFieldObject,
false);
920 if (optionalError.has_value()) [[unlikely]] {
921 auto error = optionalError.value();
922 error.addCurrentLocationToErrorStack();
925 }
else if (pSerializeProperty->getSerializationType() == FST_AS_EXTERNAL_BINARY_FILE) {
927 bool bDeserialized =
false;
928 for (
const auto& pBinarySerializer : vBinaryFieldSerializers) {
929 if (!pBinarySerializer->isFieldTypeSupported(pField)) {
934 auto optionalError = pBinarySerializer->deserializeField(
935 pathToExternalFile, pSmartPointerInstance.get(), pField);
936 if (optionalError.has_value()) [[unlikely]] {
937 auto error = optionalError.value();
938 error.addCurrentLocationToErrorStack();
943 bDeserialized =
true;
948 if (!bDeserialized) [[unlikely]] {
949 return Error(std::format(
950 "the field \"{}\" with type \"{}\" (maybe inherited) has "
951 "unsupported for deserialization type",
953 pField->getCanonicalTypeName()));
957 pFieldObject->onAfterDeserialized();
958 }
else [[unlikely]] {
959 return Error(std::format(
"unhandled case on field \"{}\"", pField->getName()));
966 bool bFoundSerializer =
false;
967 for (
const auto& pSerializer : vFieldSerializers) {
968 if (pSerializer->isFieldTypeSupported(pField)) {
969 bFoundSerializer =
true;
970 auto optionalError = pSerializer->deserializeField(
973 pSmartPointerInstance.get(),
978 if (optionalError.has_value()) [[unlikely]] {
979 auto error = optionalError.value();
980 error.addCurrentLocationToErrorStack();
981 if (pOriginalEntity) {
983 "an error occurred while deserializing "
984 "changed field (this field was not deserialized), error: {}",
985 error.getFullErrorMessage()));
993 if (!bFoundSerializer) [[unlikely]] {
995 std::format(
"unable to find a deserializer that supports field \"{}\"", sFieldName));
999 if (optionalPathToFile.has_value() &&
1000 optionalPathToFile->string().starts_with(
1003 auto sRelativePath = std::filesystem::relative(
1004 optionalPathToFile->string(),
1009 std::replace(sRelativePath.begin(), sRelativePath.end(),
'\\',
'/');
1012 if (sRelativePath.starts_with(
'/')) {
1013 sRelativePath = sRelativePath.substr(1);
1017 const auto pathToOriginalFile =
1019 if (!std::filesystem::exists(pathToOriginalFile)) [[unlikely]] {
1020 return Error(std::format(
1021 "failed to save the relative path to the `res` directory for the file at \"{}\", "
1022 "reason: constructed path \"{}\" does not exist",
1023 optionalPathToFile->string(),
1024 pathToOriginalFile.string()));
1028 pSmartPointerInstance->pathDeserializedFromRelativeToRes = {sRelativePath, sEntityId};
1035 return pSmartPointerInstance;
1040File_Serializable_GENERATED
static std::vector< IFieldSerializer * > getFieldSerializers()
Definition: FieldSerializerManager.cpp:58
static std::vector< IBinaryFieldSerializer * > getBinaryFieldSerializers()
Definition: FieldSerializerManager.cpp:69
Definition: GuidProperty.h:30
void error(std::string_view sText, const std::source_location location=std::source_location::current()) const
Definition: Logger.cpp:75
static Logger & get()
Definition: Logger.cpp:41
void warn(std::string_view sText, const std::source_location location=std::source_location::current()) const
Definition: Logger.cpp:62
static std::filesystem::path getPathToResDirectory()
Definition: ProjectPaths.cpp:123
Definition: SerializableObjectFieldSerializer.h:19
static bool isFieldSerializable(rfk::Field const &field)
Definition: SerializableObjectFieldSerializer.cpp:393
static bool isDerivedFromSerializable(rfk::Archetype const *pArchetype)
Definition: SerializableObjectFieldSerializer.cpp:410
static std::optional< Error > cloneSerializableObject(Serializable *pFrom, Serializable *pTo, bool bNotifyAboutDeserialized)
Definition: SerializableObjectFieldSerializer.cpp:52
Definition: Serializable.h:113
virtual void onAfterDeserialized()
Definition: Serializable.h:421
static std::variant< SmartPointer, Error > deserialize(const std::filesystem::path &pathToFile)
Definition: Serializable.h:521
static std::variant< std::vector< DeserializedObjectInformation< SmartPointer > >, Error > deserializeMultiple(std::filesystem::path pathToFile)
Definition: Serializable.h:550
std::optional< std::pair< std::string, std::string > > pathDeserializedFromRelativeToRes
Definition: Serializable.h:497
static std::optional< Error > resolvePathToToml(std::filesystem::path &pathToFile)
Definition: Serializable.cpp:651
static std::variant< SmartPointer, Error > deserializeFromSection(const toml::value &tomlData, std::unordered_map< std::string, std::string > &customAttributes, const std::string &sSectionName, const std::string &sTypeGuid, const std::string &sEntityId, const std::optional< std::filesystem::path > &optionalPathToFile)
Definition: Serializable.h:712
Definition: SerializeProperty.h:42