Nameless Engine
Loading...
Searching...
No Matches
Serializable.h
1#pragma once
2
3// Standard.
4#include <filesystem>
5#include <memory>
6#include <variant>
7#include <optional>
8#include <set>
9#include <format>
10
11// Custom.
12#include "misc/Error.h"
13#include "io/Logger.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"
20
21// External.
22#include "Refureku/Object.h"
23#include "GcPtr.h"
24
25#include "Serializable.generated.h"
26
27namespace ne RNAMESPACE() {
28 class Serializable;
29
32 public:
34
47 const std::string& sObjectUniqueId,
48 const std::unordered_map<std::string, std::string>& customAttributes = {},
49 Serializable* pOriginalObject = nullptr) {
50 this->pObject = pObject;
51 this->sObjectUniqueId = sObjectUniqueId;
53 this->pOriginalObject = pOriginalObject;
54 }
55
58
65
67 std::string sObjectUniqueId;
68
70 std::unordered_map<std::string, std::string> customAttributes;
71 };
72
74 template <typename SmartPointer, typename InnerType = typename SmartPointer::element_type>
75 requires std::same_as<SmartPointer, sgc::GcPtr<Serializable>> ||
76 std::same_as<SmartPointer, std::unique_ptr<Serializable>>
78 public:
80
89 SmartPointer pObject,
90 std::string sObjectUniqueId,
91 std::unordered_map<std::string, std::string> customAttributes) {
92 this->pObject = std::move(pObject);
93 this->sObjectUniqueId = sObjectUniqueId;
94 this->customAttributes = customAttributes;
95 }
96
98 SmartPointer pObject;
99
101 std::string sObjectUniqueId;
102
104 std::unordered_map<std::string, std::string> customAttributes;
105 };
106
113 class RCLASS(Guid("f5a59b47-ead8-4da4-892e-cf05abb2f3cc")) Serializable : public rfk::Object {
114 // This field serializer will call `onAfterDeserialized` after deserialization.
116
117 public:
118 Serializable() = default;
119 virtual ~Serializable() override = default;
120
167 [[nodiscard]] std::optional<Error> serialize(
168 std::filesystem::path pathToFile,
169 bool bEnableBackup,
170 const std::unordered_map<std::string, std::string>& customAttributes = {});
171
191 [[nodiscard]] static std::optional<Error> serializeMultiple(
192 std::filesystem::path pathToFile,
193 std::vector<SerializableObjectInformation> vObjects,
194 bool bEnableBackup);
195
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);
222
247 [[nodiscard]] std::variant<std::string, Error> serialize(
248 toml::value& tomlData,
249 Serializable* pOriginalObject,
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);
254
264 static std::variant<std::set<std::string>, Error> getIdsFromFile(std::filesystem::path pathToFile);
265
279 template <typename SmartPointer, typename InnerType = typename SmartPointer::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);
284
298 template <typename SmartPointer, typename InnerType = typename SmartPointer::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);
305
321 template <typename SmartPointer, typename InnerType = typename SmartPointer::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);
329
344 template <typename SmartPointer, typename InnerType = typename SmartPointer::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);
350
361 template <typename SmartPointer, typename InnerType = typename SmartPointer::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);
366
387 template <typename SmartPointer, typename InnerType = typename SmartPointer::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 = {});
396
412 std::optional<std::pair<std::string, std::string>> getPathDeserializedFromRelativeToRes() const;
413
414 protected:
422 virtual void onAfterDeserialized() {}
423
424 private:
434 [[nodiscard]] static std::optional<Error> resolvePathToToml(std::filesystem::path& pathToFile);
435
443 static const rfk::Class* getClassForGuid(const std::string& sGuid);
444
454 static const rfk::Struct*
455 getClassForGuid(const rfk::Struct* pArchetypeToAnalyze, const std::string& sGuid);
456
469 std::optional<std::pair<std::string, std::string>> pathDeserializedFromRelativeToRes;
470
472 static inline const auto sSubEntityFieldNameKey = ".field_name";
473
478 static inline const auto sPathRelativeToResKey = ".path_relative_to_res";
479
481 static inline const auto sNothingToSerializeKey = ".none";
482
484 static constexpr std::string_view sCustomAttributePrefix = "..";
485
486 ne_Serializable_GENERATED
487 };
488
489 template <typename SmartPointer, 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>>)
493 std::variant<SmartPointer, Error> Serializable::deserialize(const std::filesystem::path& pathToFile) {
494 std::unordered_map<std::string, std::string> foundCustomAttributes;
495 return deserialize<SmartPointer>(pathToFile, foundCustomAttributes);
496 }
497
498 template <typename SmartPointer, 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>>)
502 std::variant<SmartPointer, Error> Serializable::deserialize(
503 const std::filesystem::path& pathToFile,
504 std::unordered_map<std::string, std::string>& customAttributes) {
505 return deserialize<SmartPointer>(pathToFile, customAttributes, "");
506 }
507
508 template <typename SmartPointer, 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>>)
512 std::variant<SmartPointer, Error> Serializable::deserialize(
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);
516 }
517
518 template <typename SmartPointer, 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>
522 Serializable::deserializeMultiple(std::filesystem::path pathToFile, const std::set<std::string>& ids) {
523 // Check that specified IDs don't have dots in them.
524 for (const auto& sId : ids) {
525 if (sId.contains('.')) [[unlikely]] {
526 return Error(
527 std::format("the specified object ID \"{}\" is not allowed to have dots in it", sId));
528 }
529 }
530
531 // Resolve path.
532 auto optionalError = resolvePathToToml(pathToFile);
533 if (optionalError.has_value()) [[unlikely]] {
534 auto error = std::move(optionalError.value());
535 error.addCurrentLocationToErrorStack();
536 return error;
537 }
538
539 // Parse file.
540 toml::value tomlData;
541 try {
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()));
546 }
547
548 // Deserialize.
549 std::vector<DeserializedObjectInformation<SmartPointer>> deserializedObjects;
550 for (const auto& sEntityId : ids) {
551 // Deserialize object with the specified entity ID.
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();
557 return error;
558 }
559
560 // Save object info.
562 std::get<SmartPointer>(std::move(result)), sEntityId, customAttributes);
563 deserializedObjects.push_back(std::move(objectInfo));
564 }
565
566 return deserializedObjects;
567 }
568
569 template <typename SmartPointer, 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>>)
573 std::variant<SmartPointer, Error> Serializable::deserialize(
574 std::filesystem::path pathToFile,
575 std::unordered_map<std::string, std::string>& customAttributes,
576 const std::string& sEntityId) {
577 // Resolve path.
578 auto optionalError = resolvePathToToml(pathToFile);
579 if (optionalError.has_value()) [[unlikely]] {
580 auto error = std::move(optionalError.value());
581 error.addCurrentLocationToErrorStack();
582 return error;
583 }
584
585 // Parse file.
586 toml::value tomlData;
587 try {
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()));
592 }
593
594 // Deserialize.
595 return deserialize<SmartPointer>(tomlData, customAttributes, sEntityId, pathToFile);
596 }
597
598 template <typename SmartPointer, 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>>)
602 std::variant<SmartPointer, Error> Serializable::deserialize(
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()) {
608 // Put something as entity ID so it would not look weird.
609 sEntityId = "0";
610 }
611
612 // Read TOML as table.
613 const auto fileTable = tomlData.as_table();
614
615 // Check that we have at least one section.
616 if (fileTable.empty()) [[unlikely]] {
617 return Error("provided toml value has 0 sections while expected at least 1 section");
618 }
619
620 // Find a section that starts with the specified entity ID.
621 // Each entity section has the following format: [entityId.GUID]
622 // For sub entities (field with reflected type) format: [parentEntityId.childEntityId.childGUID]
623 std::string sTargetSection;
624 std::string sTypeGuid;
625 for (const auto& [sSectionName, value] : fileTable) {
626 // We can't just use `sSectionName.starts_with(sEntityId)` because we might make a mistake in the
627 // following situation: [100...] with entity ID equal to "10" and even if we add a dot
628 // to `sEntityId` we still might make a mistake in the following situation:
629 // [10.30.GUID] while we look for just [10.GUID].
630
631 // Get ID end position (GUID start position).
632 const auto iIdEndDotPos = sSectionName.rfind('.');
633 if (iIdEndDotPos == std::string::npos) [[unlikely]] {
634 return Error("provided toml value does not contain entity ID");
635 }
636 if (iIdEndDotPos + 1 == sSectionName.size()) [[unlikely]] {
637 return Error(std::format("section name \"{}\" does not have a GUID", sSectionName));
638 }
639 if (iIdEndDotPos == 0) [[unlikely]] {
640 return Error(std::format("section \"{}\" is not full", sSectionName));
641 }
642
643 // Get ID chain (either entity ID or something like "parentEntityId.childEntityId").
644 if (std::string_view(sSectionName.data(), iIdEndDotPos) != sEntityId) { // compare without copy
645 continue;
646 }
647
648 // Save target section name.
649 sTargetSection = sSectionName;
650
651 // Save this entity's GUID.
652 sTypeGuid = sSectionName.substr(iIdEndDotPos + 1);
653
654 break;
655 }
656
657 // Make sure something was found.
658 if (sTargetSection.empty()) [[unlikely]] {
659 return Error(std::format("could not find entity with ID \"{}\"", sEntityId));
660 }
661
662 // Get all keys (field names) from this section.
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));
666 }
667
668 // Collect keys from target section.
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) {
674 continue;
675 } else if (sKey == sPathRelativeToResKey) {
676 // Make sure the value is string.
677 if (!value.is_string()) [[unlikely]] {
678 return Error(
679 std::format("found \"{}\" key's value is not string", sPathRelativeToResKey));
680 }
681
682 // Deserialize original entity.
683 auto deserializeResult = Serializable::deserialize<SmartPointer>(
684 ProjectPaths::getPathToResDirectory(ResourceDirectory::ROOT) / value.as_string().str);
685 if (std::holds_alternative<Error>(deserializeResult)) [[unlikely]] {
686 auto error = std::get<Error>(std::move(deserializeResult));
687 error.addCurrentLocationToErrorStack();
688 return error;
689 }
690 pOriginalEntity = std::get<SmartPointer>(std::move(deserializeResult));
691 } else if (sKey.starts_with(sCustomAttributePrefix)) {
692 // Custom attribute.
693 // Make sure it's a string.
694 if (!value.is_string()) [[unlikely]] {
695 return Error(std::format("found custom attribute \"{}\" is not a string", sKey));
696 }
697
698 // Add attribute.
699 customAttributes[sKey.substr(sCustomAttributePrefix.size())] = value.as_string().str;
700 } else {
701 fieldsToDeserialize[sKey] = &value;
702 }
703 }
704
705 // Get archetype for found GUID.
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",
712 sTypeGuid,
713 pOriginalEntity->getPathDeserializedFromRelativeToRes().value().first,
714 pOriginalEntity->getPathDeserializedFromRelativeToRes().value().second));
715 } else {
716 return Error(std::format("no type found for GUID \"{}\"", sTypeGuid));
717 }
718 }
719
720 // Make sure the type indeed derives from serializable.
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",
726 sTypeGuid,
727 staticGetArchetype().getName(),
728 pOriginalEntity->getPathDeserializedFromRelativeToRes().value().first,
729 pOriginalEntity->getPathDeserializedFromRelativeToRes().value().second));
730 } else {
731 return Error(std::format(
732 "deserialized type with GUID \"{}\" does not derive from {}",
733 sTypeGuid,
734 staticGetArchetype().getName()));
735 }
736 }
737
738 // Create instance.
739 SmartPointer pSmartPointerInstance = nullptr;
740 if (pOriginalEntity != nullptr) {
741 // Use the original entity instead of creating a new one.
742 pSmartPointerInstance = std::move(pOriginalEntity);
743 }
744 if (pSmartPointerInstance == nullptr) {
745 if constexpr (std::is_same_v<SmartPointer, std::unique_ptr<InnerType>>) {
746 // Create a unique instance.
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?)",
752 pType->getName(),
753 pType->getName()));
754 }
755 }
756 // NOTE: don't allow deserializing into a `shared_ptr` since it may create a false assumption
757 // that this `shared_ptr` will also save/load its referenced entity or keep its reference
758 // (you can easily convert a unique_ptr to a shared_ptr so it should not be an issue).
759 else if (std::is_same_v<SmartPointer, sgc::GcPtr<InnerType>>) {
760 // Create GC instance.
761 // (this part is a temporary solution until we will add a `makeGcFromThisType` method to
762 // `rfk::Struct`)
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?)",
768 pType->getName(),
769 pType->getName()));
770 }
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()));
778 }
779 } else [[unlikely]] {
780 return Error("unexpected smart pointer type received");
781 }
782 }
783
784 // Get field serializers.
785 const auto vFieldSerializers = FieldSerializerManager::getFieldSerializers();
786 const auto vBinaryFieldSerializers = FieldSerializerManager::getBinaryFieldSerializers();
787
788 // Deserialize fields.
789 for (auto& [sFieldName, pFieldTomlValue] : fieldsToDeserialize) {
790 if (sFieldName == sSubEntityFieldNameKey) {
791 // This field is used as section metadata and tells us what field of parent entity
792 // this section describes.
793 continue;
794 }
795
796 // Get field by name.
797 rfk::Field const* pField =
798 pType->getFieldByName(sFieldName.data(), rfk::EFieldFlags::Default, true);
799 if (pField == nullptr) [[unlikely]] { // rarely happens
800 Logger::get().warn(std::format(
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)",
804 sFieldName));
805 continue;
806 }
807 const auto sFieldCanonicalTypeName = std::string(pField->getCanonicalTypeName());
808
809 // Check if it's serializable.
811 continue;
812 }
813
814 // Check if we need to deserialize from external file.
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) {
819 // Make sure this field derives from `Serializable`.
821 pField->getType().getArchetype())) [[unlikely]] {
822 // Show an error so that the developer will instantly see the mistake.
823 auto error = Error("only fields of type derived from `Serializable` can use "
824 "`SerializeAsExternal` property");
825 error.showError();
826 throw std::runtime_error(error.getFullErrorMessage());
827 }
828
829 // Make sure path to the main file is specified.
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");
833 }
834
835 // Prepare path to the external file.
836 if (!pFieldTomlValue->is_string()) [[unlikely]] {
837 return Error(std::format(
838 "expected field \"{}\" to store external filename in file \"{}\"",
839 pField->getName(),
840 optionalPathToFile.value().string()));
841 }
842 const auto sExternalFileName = pFieldTomlValue->as_string().str;
843 const auto pathToExternalFile = optionalPathToFile.value().parent_path() / sExternalFileName;
844
845 // Get field object.
846 const auto pFieldObject =
847 reinterpret_cast<Serializable*>(pField->getPtrUnsafe(pSmartPointerInstance.get()));
848
849 if (pSerializeProperty->getSerializationType() == FST_AS_EXTERNAL_FILE) {
850 // Deserialize 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();
855 return error;
856 }
857 const auto pDeserializedExternalField =
858 std::get<std::unique_ptr<Serializable>>(std::move(result));
859
860 // Clone deserialized data to field.
862 pDeserializedExternalField.get(), pFieldObject, false);
863 if (optionalError.has_value()) [[unlikely]] {
864 auto error = optionalError.value();
865 error.addCurrentLocationToErrorStack();
866 return error;
867 }
868 } else if (pSerializeProperty->getSerializationType() == FST_AS_EXTERNAL_BINARY_FILE) {
869 // Deserialize binary file.
870 bool bDeserialized = false;
871 for (const auto& pBinarySerializer : vBinaryFieldSerializers) {
872 if (!pBinarySerializer->isFieldTypeSupported(pField)) {
873 continue;
874 }
875
876 // Deserialize as binary.
877 auto optionalError = pBinarySerializer->deserializeField(
878 pathToExternalFile, pSmartPointerInstance.get(), pField);
879 if (optionalError.has_value()) [[unlikely]] {
880 auto error = optionalError.value();
881 error.addCurrentLocationToErrorStack();
882 return error;
883 }
884
885 // Finished.
886 bDeserialized = true;
887 break;
888 }
889
890 // Make sure we deserialized this field.
891 if (!bDeserialized) [[unlikely]] {
892 return Error(std::format(
893 "the field \"{}\" with type \"{}\" (maybe inherited) has "
894 "unsupported for deserialization type",
895 pField->getName(),
896 pField->getCanonicalTypeName()));
897 }
898
899 // Notify about deserialization.
900 pFieldObject->onAfterDeserialized();
901 } else [[unlikely]] {
902 return Error(std::format("unhandled case on field \"{}\"", pField->getName()));
903 }
904
905 continue;
906 }
907
908 // Deserialize field value.
909 bool bFoundSerializer = false;
910 for (const auto& pSerializer : vFieldSerializers) {
911 if (pSerializer->isFieldTypeSupported(pField)) {
912 bFoundSerializer = true;
913 auto optionalError = pSerializer->deserializeField(
914 &tomlData,
915 pFieldTomlValue,
916 pSmartPointerInstance.get(),
917 pField,
918 sTargetSection,
919 sEntityId,
920 customAttributes);
921 if (optionalError.has_value()) [[unlikely]] {
922 auto error = optionalError.value();
923 error.addCurrentLocationToErrorStack();
924 if (pOriginalEntity) {
925 Logger::get().error(std::format(
926 "an error occurred while deserializing "
927 "changed field (this field was not deserialized), error: {}",
928 error.getFullErrorMessage()));
929 } else {
930 return error;
931 }
932 }
933 }
934 }
935
936 if (!bFoundSerializer) [[unlikely]] {
938 std::format("unable to find a deserializer that supports field \"{}\"", sFieldName));
939 }
940 }
941
942 if (optionalPathToFile.has_value() &&
943 optionalPathToFile->string().starts_with(
944 ProjectPaths::getPathToResDirectory(ResourceDirectory::ROOT).string())) {
945 // File is located in the `res` directory, save a relative path to the `res` directory.
946 auto sRelativePath = std::filesystem::relative(
947 optionalPathToFile->string(),
948 ProjectPaths::getPathToResDirectory(ResourceDirectory::ROOT))
949 .string();
950
951 // Replace all '\' characters with '/' just to be consistent.
952 std::replace(sRelativePath.begin(), sRelativePath.end(), '\\', '/');
953
954 // Remove the forward slash at the beginning (if exists).
955 if (sRelativePath.starts_with('/')) {
956 sRelativePath = sRelativePath.substr(1);
957 }
958
959 // Double check that everything is correct.
960 const auto pathToOriginalFile =
961 ProjectPaths::getPathToResDirectory(ResourceDirectory::ROOT) / sRelativePath;
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()));
968 }
969
970 // Save deserialization path.
971 pSmartPointerInstance->pathDeserializedFromRelativeToRes = {sRelativePath, sEntityId};
972 }
973
974 // Notify about deserialization finished.
975 Serializable* pTarget = dynamic_cast<Serializable*>(pSmartPointerInstance.get());
976 pTarget->onAfterDeserialized();
977
978 return pSmartPointerInstance;
979 }
980
981}; // namespace ne RNAMESPACE()
982
983File_Serializable_GENERATED
Definition: Error.h:27
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
Definition: Serializable.h:77
SmartPointer pObject
Definition: Serializable.h:98
DeserializedObjectInformation(SmartPointer pObject, std::string sObjectUniqueId, std::unordered_map< std::string, std::string > customAttributes)
Definition: Serializable.h:88
std::unordered_map< std::string, std::string > customAttributes
Definition: Serializable.h:104
std::string sObjectUniqueId
Definition: Serializable.h:101
Definition: Serializable.h:31
Serializable * pObject
Definition: Serializable.h:57
std::string sObjectUniqueId
Definition: Serializable.h:67
Serializable * pOriginalObject
Definition: Serializable.h:64
std::unordered_map< std::string, std::string > customAttributes
Definition: Serializable.h:70
SerializableObjectInformation(Serializable *pObject, const std::string &sObjectUniqueId, const std::unordered_map< std::string, std::string > &customAttributes={}, Serializable *pOriginalObject=nullptr)
Definition: Serializable.h:45