#pragma once #include #include #include #include #include #include #include #include #include #include "nf7.hh" #include "common/file_base.hh" #include "common/generic_watcher.hh" #include "common/memento.hh" #include "common/yas_nf7.hh" #include "common/yas_std_variant.hh" namespace nf7 { class FileHolder : public nf7::FileBase::Feature { public: class Tag; class EmptyException final : public nf7::Exception { public: using nf7::Exception::Exception; }; using Entity = std::variant< std::monostate, nf7::File::Path, std::shared_ptr>; FileHolder(nf7::File& owner, std::string_view id) noexcept : owner_(&owner), id_(id) { } FileHolder(const FileHolder&) = delete; FileHolder(FileHolder&&) = delete; FileHolder& operator=(const FileHolder&) = delete; FileHolder& operator=(FileHolder&&) = delete; void Serialize(auto& ar) const { ar(entity_); } void Deserialize(auto& ar) { ar(entity_); SetUp(); } void Emplace(nf7::File::Path&& path) noexcept { TearDown(); tag_ = nullptr; entity_ = std::move(path); SetUp(); onEmplace(); } void Emplace(std::unique_ptr&& f) noexcept { TearDown(); tag_ = nullptr; entity_ = std::move(f); SetUp(); onEmplace(); } nf7::File& GetFileOrThrow() { if (auto f = GetFile()) { return *f; } throw EmptyException {"holder is empty"}; } nf7::File* GetFile() noexcept { SetUp(); return file_; } // nf7::FileBase::Feature methods nf7::File* Find(std::string_view name) const noexcept override; void Handle(const nf7::File::Event&) noexcept override; void Update() noexcept override; bool own() const noexcept { return std::holds_alternative>(entity_); } bool ref() const noexcept { return std::holds_alternative(entity_); } bool empty() const noexcept { return std::holds_alternative(entity_); } nf7::File& owner() const noexcept { return *owner_; } nf7::Env& env() const noexcept { return owner_->env(); } const std::string& id() const noexcept { return id_; } nf7::File* file() const noexcept { return file_; } nf7::File::Path path() const noexcept { assert(!empty()); return own()? nf7::File::Path {{id_}}: std::get(entity_); } // called when kUpdate event is caused by the child std::function onChildUpdate = [](){}; // called when the child's memento tag id is changed std::function onChildMementoChange = [](){}; // called right before returning from Emplace() std::function onEmplace = [](){}; private: nf7::File* const owner_; const std::string id_; Entity entity_; std::shared_ptr tag_; nf7::File* file_ = nullptr; std::optional watcher_; void SetUp() noexcept; void TearDown() noexcept; }; // to save/restore FileHolder's changes through GenericMemento class FileHolder::Tag final { public: Tag() = default; Tag(const Tag&) noexcept; Tag& operator=(const Tag&) noexcept; Tag(Tag&&) = default; Tag& operator=(Tag&&) = default; void SetTarget(nf7::FileHolder& h) noexcept; private: nf7::FileHolder* target_ = nullptr; Entity entity_; std::shared_ptr tag_; }; } // namespace nf7 namespace yas::detail { template struct serializer< type_prop::not_a_fundamental, ser_case::use_internal_serializer, F, nf7::FileHolder> { public: template static Archive& save(Archive& ar, const nf7::FileHolder& h) { h.Serialize(ar); return ar; } template static Archive& load(Archive& ar, nf7::FileHolder& h) { h.Deserialize(ar); return ar; } }; } // namespace yas::detail