2023-10-02 20:36:53 +00:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
|
|
*/
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include <AK/ByteBuffer.h>
|
|
|
|
#include <AK/Error.h>
|
|
|
|
#include <AK/MemoryStream.h>
|
|
|
|
#include <AK/RefPtr.h>
|
|
|
|
#include <AK/Span.h>
|
|
|
|
#include <AK/String.h>
|
|
|
|
#include <AK/StringView.h>
|
|
|
|
#include <AK/Variant.h>
|
|
|
|
#include <LibCore/File.h>
|
|
|
|
#include <LibCore/MappedFile.h>
|
|
|
|
|
|
|
|
namespace Core {
|
2023-11-05 13:05:22 +00:00
|
|
|
|
2023-10-02 20:36:53 +00:00
|
|
|
class Resource : public RefCounted<Resource> {
|
|
|
|
public:
|
2023-10-18 20:08:44 +00:00
|
|
|
static ErrorOr<NonnullRefPtr<Resource>> load_from_filesystem(StringView);
|
2023-10-02 20:36:53 +00:00
|
|
|
static ErrorOr<NonnullRefPtr<Resource>> load_from_uri(StringView);
|
|
|
|
|
|
|
|
[[nodiscard]] bool is_file() const { return !m_data.has<DirectoryTag>(); }
|
|
|
|
[[nodiscard]] bool is_directory() const { return m_data.has<DirectoryTag>(); }
|
|
|
|
|
|
|
|
[[nodiscard]] String uri() const;
|
|
|
|
[[nodiscard]] String filename() const;
|
2023-11-05 13:05:22 +00:00
|
|
|
[[nodiscard]] String filesystem_path() const;
|
2023-11-05 13:10:07 +00:00
|
|
|
[[nodiscard]] String file_url() const;
|
2023-10-02 20:36:53 +00:00
|
|
|
|
|
|
|
[[nodiscard]] ByteBuffer clone_data() const;
|
|
|
|
[[nodiscard]] ByteBuffer release_data() &&;
|
|
|
|
[[nodiscard]] ReadonlyBytes data() const;
|
|
|
|
[[nodiscard]] FixedMemoryStream stream() const;
|
|
|
|
|
|
|
|
[[nodiscard]] Vector<String> children() const;
|
|
|
|
// Depth-first
|
|
|
|
template<IteratorFunction<Resource const&> Callback>
|
|
|
|
IterationDecision for_each_descendant(Callback&&) const;
|
|
|
|
|
|
|
|
template<IteratorFunction<Resource const&> Callback>
|
|
|
|
void for_each_descendant_file(Callback&&) const;
|
|
|
|
|
|
|
|
struct DirectoryTag { };
|
|
|
|
|
|
|
|
private:
|
|
|
|
friend class ResourceImplementation;
|
|
|
|
|
|
|
|
enum class Scheme {
|
|
|
|
File,
|
|
|
|
Resource,
|
|
|
|
};
|
|
|
|
|
|
|
|
Resource(String path, Scheme, NonnullOwnPtr<Core::MappedFile>);
|
|
|
|
Resource(String path, Scheme, ByteBuffer);
|
|
|
|
Resource(String path, Scheme, DirectoryTag);
|
|
|
|
|
|
|
|
String m_path; // Relative to scheme root. File: abspath, Resource: resource root
|
|
|
|
Scheme m_scheme;
|
|
|
|
|
|
|
|
Variant<DirectoryTag, NonnullOwnPtr<Core::MappedFile>, ByteBuffer> m_data;
|
|
|
|
};
|
|
|
|
|
|
|
|
template<IteratorFunction<Resource const&> 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<IteratorFunction<Resource const&> Callback>
|
|
|
|
void Resource::for_each_descendant_file(Callback&& callback) const
|
|
|
|
{
|
|
|
|
for_each_descendant([callback = forward<Callback>(callback)](Resource const& resource) {
|
|
|
|
if (resource.is_directory())
|
|
|
|
return IterationDecision::Continue;
|
|
|
|
return callback(resource);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|