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 {
167 [[nodiscard]] std::optional<Error> serialize(
168 std::filesystem::path pathToFile,
170 const std::unordered_map<std::string, std::string>& customAttributes = {});
191 [[nodiscard]]
static std::optional<Error> serializeMultiple(
192 std::filesystem::path pathToFile,
193 std::vector<SerializableObjectInformation> vObjects,
216 [[nodiscard]] std::variant<std::string, Error> serialize(
217 toml::value& tomlData,
218 const std::string& sEntityId =
"",
219 const std::unordered_map<std::string, std::string>& customAttributes = {},
220 const std::optional<std::filesystem::path>& optionalPathToFile = {},
221 bool bEnableBackup =
false);
247 [[nodiscard]] std::variant<std::string, Error> serialize(
248 toml::value& tomlData,
250 std::string sEntityId =
"",
251 const std::unordered_map<std::string, std::string>& customAttributes = {},
252 const std::optional<std::filesystem::path>& optionalPathToFile = {},
253 bool bEnableBackup =
false);
264 static std::variant<std::set<std::string>,
Error> getIdsFromFile(std::filesystem::path pathToFile);
279 template <
typename SmartPo
inter,
typename InnerType =
typename SmartPo
inter::element_type>
280 requires std::derived_from<InnerType, Serializable> &&
281 (std::same_as<SmartPointer, sgc::GcPtr<InnerType>> ||
282 std::same_as<SmartPointer, std::unique_ptr<InnerType>>)
283 static std::variant<SmartPointer, Error> deserialize(
const std::filesystem::path& pathToFile);
298 template <
typename SmartPo
inter,
typename InnerType =
typename SmartPo
inter::element_type>
299 requires std::derived_from<InnerType, Serializable> &&
300 (std::same_as<SmartPointer, sgc::GcPtr<InnerType>> ||
301 std::same_as<SmartPointer, std::unique_ptr<InnerType>>)
302 static std::variant<SmartPointer, Error> deserialize(
303 const std::filesystem::path& pathToFile,
304 std::unordered_map<std::string, std::string>& customAttributes);
321 template <
typename SmartPo
inter,
typename InnerType =
typename SmartPo
inter::element_type>
322 requires std::derived_from<InnerType, Serializable> &&
323 (std::same_as<SmartPointer, sgc::GcPtr<InnerType>> ||
324 std::same_as<SmartPointer, std::unique_ptr<InnerType>>)
325 static std::variant<SmartPointer, Error> deserialize(
326 std::filesystem::path pathToFile,
327 std::unordered_map<std::string, std::string>& customAttributes,
328 const std::string& sEntityId);
344 template <
typename SmartPo
inter,
typename InnerType =
typename SmartPo
inter::element_type>
345 requires std::derived_from<InnerType, Serializable> &&
346 (std::same_as<SmartPointer, sgc::GcPtr<InnerType>> ||
347 std::same_as<SmartPointer, std::unique_ptr<InnerType>>)
348 static std::variant<SmartPointer, Error> deserialize(
349 const std::filesystem::path& pathToFile,
const std::string& sEntityId);
361 template <
typename SmartPo
inter,
typename InnerType =
typename SmartPo
inter::element_type>
362 requires std::same_as<SmartPointer, sgc::GcPtr<Serializable>> ||
363 std::same_as<SmartPointer, std::unique_ptr<Serializable>>
364 static std::variant<std::vector<DeserializedObjectInformation<SmartPointer>>,
Error>
365 deserializeMultiple(std::filesystem::path pathToFile,
const std::set<std::string>& ids);
387 template <
typename SmartPo
inter,
typename InnerType =
typename SmartPo
inter::element_type>
388 requires std::derived_from<InnerType, Serializable> &&
389 (std::same_as<SmartPointer, sgc::GcPtr<InnerType>> ||
390 std::same_as<SmartPointer, std::unique_ptr<InnerType>>)
391 static std::variant<SmartPointer, Error> deserialize(
392 const toml::value& tomlData,
393 std::unordered_map<std::string, std::string>& customAttributes,
394 std::string sEntityId =
"",
395 std::optional<std::filesystem::path> optionalPathToFile = {});
412 std::optional<std::pair<std::string, std::string>> getPathDeserializedFromRelativeToRes()
const;
434 [[nodiscard]]
static std::optional<Error> resolvePathToToml(std::filesystem::path& pathToFile);
443 static const rfk::Class* getClassForGuid(
const std::string& sGuid);
454 static const rfk::Struct*
455 getClassForGuid(
const rfk::Struct* pArchetypeToAnalyze,
const std::string& sGuid);
472 static inline const auto sSubEntityFieldNameKey =
".field_name";
478 static inline const auto sPathRelativeToResKey =
".path_relative_to_res";
481 static inline const auto sNothingToSerializeKey =
".none";
484 static constexpr std::string_view sCustomAttributePrefix =
"..";
486 ne_Serializable_GENERATED
489 template <
typename SmartPo
inter,
typename InnerType>
490 requires std::derived_from<InnerType, Serializable> &&
491 (std::same_as<SmartPointer, sgc::GcPtr<InnerType>> ||
492 std::same_as<SmartPointer, std::unique_ptr<InnerType>>)
494 std::unordered_map<std::string, std::string> foundCustomAttributes;
495 return deserialize<SmartPointer>(pathToFile, foundCustomAttributes);
498 template <
typename SmartPo
inter,
typename InnerType>
499 requires std::derived_from<InnerType, Serializable> &&
500 (std::same_as<SmartPointer, sgc::GcPtr<InnerType>> ||
501 std::same_as<SmartPointer, std::unique_ptr<InnerType>>)
503 const std::filesystem::path& pathToFile,
504 std::unordered_map<std::string, std::string>& customAttributes) {
505 return deserialize<SmartPointer>(pathToFile, customAttributes,
"");
508 template <
typename SmartPo
inter,
typename InnerType>
509 requires std::derived_from<InnerType, Serializable> &&
510 (std::same_as<SmartPointer, sgc::GcPtr<InnerType>> ||
511 std::same_as<SmartPointer, std::unique_ptr<InnerType>>)
513 const std::filesystem::path& pathToFile,
const std::string& sEntityId) {
514 std::unordered_map<std::string, std::string> foundCustomAttributes;
515 return deserialize<SmartPointer>(pathToFile, foundCustomAttributes, sEntityId);
518 template <
typename SmartPo
inter,
typename InnerType>
519 requires std::same_as<SmartPointer, sgc::GcPtr<Serializable>> ||
520 std::same_as<SmartPointer, std::unique_ptr<Serializable>>
521 std::variant<std::vector<DeserializedObjectInformation<SmartPointer>>,
Error>
524 for (
const auto& sId : ids) {
525 if (sId.contains(
'.')) [[unlikely]] {
527 std::format(
"the specified object ID \"{}\" is not allowed to have dots in it", sId));
533 if (optionalError.has_value()) [[unlikely]] {
534 auto error = std::move(optionalError.value());
535 error.addCurrentLocationToErrorStack();
540 toml::value tomlData;
542 tomlData = toml::parse(pathToFile);
543 }
catch (std::exception& exception) {
544 return Error(std::format(
545 "failed to parse TOML file at \"{}\", error: {}", pathToFile.string(), exception.what()));
549 std::vector<DeserializedObjectInformation<SmartPointer>> deserializedObjects;
550 for (
const auto& sEntityId : ids) {
552 std::unordered_map<std::string, std::string> customAttributes;
553 auto result = deserialize<SmartPointer>(tomlData, customAttributes, sEntityId, pathToFile);
554 if (std::holds_alternative<Error>(result)) [[unlikely]] {
555 auto error = std::get<Error>(std::move(result));
556 error.addCurrentLocationToErrorStack();
562 std::get<SmartPointer>(std::move(result)), sEntityId, customAttributes);
563 deserializedObjects.push_back(std::move(objectInfo));
566 return deserializedObjects;
569 template <
typename SmartPo
inter,
typename InnerType>
570 requires std::derived_from<InnerType, Serializable> &&
571 (std::same_as<SmartPointer, sgc::GcPtr<InnerType>> ||
572 std::same_as<SmartPointer, std::unique_ptr<InnerType>>)
574 std::filesystem::path pathToFile,
575 std::unordered_map<std::string, std::string>& customAttributes,
576 const std::string& sEntityId) {
578 auto optionalError = resolvePathToToml(pathToFile);
579 if (optionalError.has_value()) [[unlikely]] {
580 auto error = std::move(optionalError.value());
581 error.addCurrentLocationToErrorStack();
586 toml::value tomlData;
588 tomlData = toml::parse(pathToFile);
589 }
catch (std::exception& exception) {
590 return Error(std::format(
591 "failed to parse TOML file at \"{}\", error: {}", pathToFile.string(), exception.what()));
595 return deserialize<SmartPointer>(tomlData, customAttributes, sEntityId, pathToFile);
598 template <
typename SmartPo
inter,
typename InnerType>
599 requires std::derived_from<InnerType, Serializable> &&
600 (std::same_as<SmartPointer, sgc::GcPtr<InnerType>> ||
601 std::same_as<SmartPointer, std::unique_ptr<InnerType>>)
603 const toml::value& tomlData,
604 std::unordered_map<std::string, std::string>& customAttributes,
605 std::string sEntityId,
606 std::optional<std::filesystem::path> optionalPathToFile) {
607 if (sEntityId.empty()) {
613 const auto fileTable = tomlData.as_table();
616 if (fileTable.empty()) [[unlikely]] {
617 return Error(
"provided toml value has 0 sections while expected at least 1 section");
623 std::string sTargetSection;
624 std::string sTypeGuid;
625 for (
const auto& [sSectionName, value] : fileTable) {
632 const auto iIdEndDotPos = sSectionName.rfind(
'.');
633 if (iIdEndDotPos == std::string::npos) [[unlikely]] {
634 return Error(
"provided toml value does not contain entity ID");
636 if (iIdEndDotPos + 1 == sSectionName.size()) [[unlikely]] {
637 return Error(std::format(
"section name \"{}\" does not have a GUID", sSectionName));
639 if (iIdEndDotPos == 0) [[unlikely]] {
640 return Error(std::format(
"section \"{}\" is not full", sSectionName));
644 if (std::string_view(sSectionName.data(), iIdEndDotPos) != sEntityId) {
649 sTargetSection = sSectionName;
652 sTypeGuid = sSectionName.substr(iIdEndDotPos + 1);
658 if (sTargetSection.empty()) [[unlikely]] {
659 return Error(std::format(
"could not find entity with ID \"{}\"", sEntityId));
663 const auto& targetSection = fileTable.at(sTargetSection);
664 if (!targetSection.is_table()) [[unlikely]] {
665 return Error(std::format(
"found \"{}\" section is not a section", sTargetSection));
669 const auto& sectionTable = targetSection.as_table();
670 std::unordered_map<std::string_view, const toml::value*> fieldsToDeserialize;
671 SmartPointer pOriginalEntity =
nullptr;
672 for (
const auto& [sKey, value] : sectionTable) {
673 if (sKey == sNothingToSerializeKey) {
675 }
else if (sKey == sPathRelativeToResKey) {
677 if (!value.is_string()) [[unlikely]] {
679 std::format(
"found \"{}\" key's value is not string", sPathRelativeToResKey));
683 auto deserializeResult = Serializable::deserialize<SmartPointer>(
685 if (std::holds_alternative<Error>(deserializeResult)) [[unlikely]] {
686 auto error = std::get<Error>(std::move(deserializeResult));
687 error.addCurrentLocationToErrorStack();
690 pOriginalEntity = std::get<SmartPointer>(std::move(deserializeResult));
691 }
else if (sKey.starts_with(sCustomAttributePrefix)) {
694 if (!value.is_string()) [[unlikely]] {
695 return Error(std::format(
"found custom attribute \"{}\" is not a string", sKey));
699 customAttributes[sKey.substr(sCustomAttributePrefix.size())] = value.as_string().str;
701 fieldsToDeserialize[sKey] = &value;
706 const auto pType = getClassForGuid(sTypeGuid);
707 if (pType ==
nullptr) [[unlikely]] {
708 if (pOriginalEntity) {
709 return Error(std::format(
710 "GUID \"{}\" was not found in the database, but "
711 "the original object at \"{}\" (ID \"{}\") was deserialized",
713 pOriginalEntity->getPathDeserializedFromRelativeToRes().value().first,
714 pOriginalEntity->getPathDeserializedFromRelativeToRes().value().second));
716 return Error(std::format(
"no type found for GUID \"{}\"", sTypeGuid));
722 if (pOriginalEntity) {
723 return Error(std::format(
724 "deserialized type for \"{}\" does not derive from {}, but "
725 "the original object at \"{}\" (ID \"{}\") was deserialized",
727 staticGetArchetype().getName(),
728 pOriginalEntity->getPathDeserializedFromRelativeToRes().value().first,
729 pOriginalEntity->getPathDeserializedFromRelativeToRes().value().second));
731 return Error(std::format(
732 "deserialized type with GUID \"{}\" does not derive from {}",
734 staticGetArchetype().getName()));
739 SmartPointer pSmartPointerInstance =
nullptr;
740 if (pOriginalEntity !=
nullptr) {
742 pSmartPointerInstance = std::move(pOriginalEntity);
744 if (pSmartPointerInstance ==
nullptr) {
745 if constexpr (std::is_same_v<SmartPointer, std::unique_ptr<InnerType>>) {
747 pSmartPointerInstance = pType->makeUniqueInstance<InnerType>();
748 if (pSmartPointerInstance ==
nullptr) [[unlikely]] {
749 return Error(std::format(
750 "unable to make an object of type \"{}\" using type's default constructor "
751 "(does type \"{}\" has a default constructor?)",
759 else if (std::is_same_v<SmartPointer, sgc::GcPtr<InnerType>>) {
763 std::unique_ptr<InnerType> pInstance = pType->makeUniqueInstance<InnerType>();
764 if (pInstance ==
nullptr) [[unlikely]] {
765 return Error(std::format(
766 "unable to make an object of type \"{}\" using type's default constructor "
767 "(does type \"{}\" has a default constructor?)",
771 sgc::GcPtr<rfk::Object> pParentGcInstance = pInstance->makeGcFromThisType();
772 pSmartPointerInstance =
dynamic_cast<InnerType*
>(pParentGcInstance.get());
773 if (pSmartPointerInstance ==
nullptr) [[unlikely]] {
774 return Error(std::format(
775 "dynamic cast failed to cast the type \"{}\" to the specified template argument "
776 "(are you trying to deserialize into a wrong type?)",
777 pParentGcInstance->getArchetype().getName()));
779 }
else [[unlikely]] {
780 return Error(
"unexpected smart pointer type received");
789 for (
auto& [sFieldName, pFieldTomlValue] : fieldsToDeserialize) {
790 if (sFieldName == sSubEntityFieldNameKey) {
797 rfk::Field
const* pField =
798 pType->getFieldByName(sFieldName.data(), rfk::EFieldFlags::Default,
true);
799 if (pField ==
nullptr) [[unlikely]] {
801 "field name \"{}\" exists in the specified toml value but does not exist in the "
802 "actual object (if you removed/renamed this reflected field from your "
803 "class/struct - ignore this warning)",
807 const auto sFieldCanonicalTypeName = std::string(pField->getCanonicalTypeName());
815 const auto pSerializeProperty = pField->getProperty<
Serialize>();
816 if (pSerializeProperty->getSerializationType() == FieldSerializationType::FST_AS_EXTERNAL_FILE ||
817 pSerializeProperty->getSerializationType() ==
818 FieldSerializationType::FST_AS_EXTERNAL_BINARY_FILE) {
821 pField->getType().getArchetype())) [[unlikely]] {
823 auto error =
Error(
"only fields of type derived from `Serializable` can use "
824 "`SerializeAsExternal` property");
826 throw std::runtime_error(error.getFullErrorMessage());
830 if (!optionalPathToFile.has_value()) [[unlikely]] {
831 return Error(
"unable to deserialize field marked as `SerializeAsExternal` "
832 "because path to the main file was not specified");
836 if (!pFieldTomlValue->is_string()) [[unlikely]] {
837 return Error(std::format(
838 "expected field \"{}\" to store external filename in file \"{}\"",
840 optionalPathToFile.value().string()));
842 const auto sExternalFileName = pFieldTomlValue->as_string().str;
843 const auto pathToExternalFile = optionalPathToFile.value().parent_path() / sExternalFileName;
846 const auto pFieldObject =
847 reinterpret_cast<Serializable*
>(pField->getPtrUnsafe(pSmartPointerInstance.get()));
849 if (pSerializeProperty->getSerializationType() == FST_AS_EXTERNAL_FILE) {
851 auto result = deserialize<std::unique_ptr<Serializable>>(pathToExternalFile);
852 if (std::holds_alternative<Error>(result)) [[unlikely]] {
853 auto error = std::get<Error>(std::move(result));
854 error.addCurrentLocationToErrorStack();
857 const auto pDeserializedExternalField =
858 std::get<std::unique_ptr<Serializable>>(std::move(result));
862 pDeserializedExternalField.get(), pFieldObject,
false);
863 if (optionalError.has_value()) [[unlikely]] {
864 auto error = optionalError.value();
865 error.addCurrentLocationToErrorStack();
868 }
else if (pSerializeProperty->getSerializationType() == FST_AS_EXTERNAL_BINARY_FILE) {
870 bool bDeserialized =
false;
871 for (
const auto& pBinarySerializer : vBinaryFieldSerializers) {
872 if (!pBinarySerializer->isFieldTypeSupported(pField)) {
877 auto optionalError = pBinarySerializer->deserializeField(
878 pathToExternalFile, pSmartPointerInstance.get(), pField);
879 if (optionalError.has_value()) [[unlikely]] {
880 auto error = optionalError.value();
881 error.addCurrentLocationToErrorStack();
886 bDeserialized =
true;
891 if (!bDeserialized) [[unlikely]] {
892 return Error(std::format(
893 "the field \"{}\" with type \"{}\" (maybe inherited) has "
894 "unsupported for deserialization type",
896 pField->getCanonicalTypeName()));
900 pFieldObject->onAfterDeserialized();
901 }
else [[unlikely]] {
902 return Error(std::format(
"unhandled case on field \"{}\"", pField->getName()));
909 bool bFoundSerializer =
false;
910 for (
const auto& pSerializer : vFieldSerializers) {
911 if (pSerializer->isFieldTypeSupported(pField)) {
912 bFoundSerializer =
true;
913 auto optionalError = pSerializer->deserializeField(
916 pSmartPointerInstance.get(),
921 if (optionalError.has_value()) [[unlikely]] {
922 auto error = optionalError.value();
923 error.addCurrentLocationToErrorStack();
924 if (pOriginalEntity) {
926 "an error occurred while deserializing "
927 "changed field (this field was not deserialized), error: {}",
928 error.getFullErrorMessage()));
936 if (!bFoundSerializer) [[unlikely]] {
938 std::format(
"unable to find a deserializer that supports field \"{}\"", sFieldName));
942 if (optionalPathToFile.has_value() &&
943 optionalPathToFile->string().starts_with(
946 auto sRelativePath = std::filesystem::relative(
947 optionalPathToFile->string(),
952 std::replace(sRelativePath.begin(), sRelativePath.end(),
'\\',
'/');
955 if (sRelativePath.starts_with(
'/')) {
956 sRelativePath = sRelativePath.substr(1);
960 const auto pathToOriginalFile =
962 if (!std::filesystem::exists(pathToOriginalFile)) [[unlikely]] {
963 return Error(std::format(
964 "failed to save the relative path to the `res` directory for the file at \"{}\", "
965 "reason: constructed path \"{}\" does not exist",
966 optionalPathToFile->string(),
967 pathToOriginalFile.string()));
971 pSmartPointerInstance->pathDeserializedFromRelativeToRes = {sRelativePath, sEntityId};
978 return pSmartPointerInstance;
983File_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:422
static std::variant< SmartPointer, Error > deserialize(const std::filesystem::path &pathToFile)
Definition: Serializable.h:493
std::optional< std::pair< std::string, std::string > > pathDeserializedFromRelativeToRes
Definition: Serializable.h:469
static std::optional< Error > resolvePathToToml(std::filesystem::path &pathToFile)
Definition: Serializable.cpp:663
static std::variant< std::vector< DeserializedObjectInformation< SmartPointer > >, Error > deserializeMultiple(std::filesystem::path pathToFile, const std::set< std::string > &ids)
Definition: Serializable.h:522
Definition: SerializeProperty.h:42