/* * Copyright (c) 2023, Andrew Kaster * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include #include #include #include #include #include namespace Core { class Resource : public RefCounted { public: static ErrorOr> load_from_filesystem(StringView); static ErrorOr> load_from_uri(StringView); [[nodiscard]] bool is_file() const { return !m_data.has(); } [[nodiscard]] bool is_directory() const { return m_data.has(); } [[nodiscard]] String uri() const; [[nodiscard]] String filename() const; [[nodiscard]] String filesystem_path() const; [[nodiscard]] String file_url() const; [[nodiscard]] Optional modified_time() const; [[nodiscard]] ByteBuffer clone_data() const; [[nodiscard]] ByteBuffer release_data() &&; [[nodiscard]] ReadonlyBytes data() const; [[nodiscard]] FixedMemoryStream stream() const; [[nodiscard]] Vector children() const; // Depth-first template Callback> IterationDecision for_each_descendant(Callback&&) const; template Callback> void for_each_descendant_file(Callback&&) const; struct DirectoryTag { }; private: friend class ResourceImplementation; enum class Scheme { File, Resource, }; Resource(String path, Scheme, NonnullOwnPtr, time_t modified_time); Resource(String path, Scheme, ByteBuffer, time_t modified_time); Resource(String path, Scheme, DirectoryTag, time_t modified_time); String m_path; // Relative to scheme root. File: abspath, Resource: resource root Scheme m_scheme; Variant, ByteBuffer> m_data; time_t m_modified_time {}; }; template Callback> IterationDecision Resource::for_each_descendant(Callback&& callback) const { auto children = this->children(); for (auto const& child : children) { if (auto child_resource = load_from_uri(MUST(String::formatted("{}/{}", uri(), child))); !child_resource.is_error()) { if (callback(*child_resource.value()) == IterationDecision::Break) return IterationDecision::Break; if (child_resource.value()->for_each_descendant(callback) == IterationDecision::Break) return IterationDecision::Break; } } return IterationDecision::Continue; } template Callback> void Resource::for_each_descendant_file(Callback&& callback) const { for_each_descendant([callback = forward(callback)](Resource const& resource) { if (resource.is_directory()) return IterationDecision::Continue; return callback(resource); }); } }