IFileSystem2.h#

Fully qualified name: carb/filesystem/detail/IFileSystem2.h

File members: carb/filesystem/detail/IFileSystem2.h

// SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: LicenseRef-NvidiaProprietary
//
// NVIDIA CORPORATION, its affiliates and licensors retain all intellectual
// property and proprietary rights in and to this material, related
// documentation and any modifications thereto. Any use, reproduction,
// disclosure or distribution of this material and related documentation
// without an express license agreement from NVIDIA CORPORATION or
// its affiliates is strictly prohibited.

#pragma once

#ifndef carb_filesystem_IFileSystem_latest
#    error Must include via ../IFileSystem1.h
#endif

#if !CARB_VERSION_ATLEAST(carb_filesystem_IFileSystem, 2, 0)
#    error Version too low to be included
#elif CARB_VERSION_ATLEAST(carb_filesystem_IFileSystem, 3, 0)
#    error Version too high to be included
#endif

#include "../../cpp/ZStringView.h"
#include "../../Error.h"
#include "../../../omni/Expected.h"
#include "../../../omni/MoveOnlyFunction.h"
#include "../../../omni/String.h"

namespace carb::filesystem
{
#ifndef DOXYGEN_BUILD
inline namespace v2
{
#endif

struct DirectoryItemInfo : public FileInfo
{
    cpp::zstring_view path;
};

struct FileTime
{
    time_t time;
    uint32_t nano;
};

#ifndef DOXYGEN_BUILD
inline bool operator==(const FileTime& lhs, const FileTime& rhs) noexcept
{
    return lhs.time == rhs.time && lhs.nano == rhs.nano;
}
inline bool operator!=(const FileTime& lhs, const FileTime& rhs) noexcept
{
    return !(lhs == rhs);
}
inline bool operator<(const FileTime& lhs, const FileTime& rhs) noexcept
{
    if (lhs.time < rhs.time)
        return true;
    return lhs.time == rhs.time ? lhs.nano < rhs.nano : false;
}
inline bool operator>(const FileTime& lhs, const FileTime& rhs) noexcept
{
    return rhs < lhs;
}
inline bool operator<=(const FileTime& lhs, const FileTime& rhs) noexcept
{
    return !(lhs > rhs);
}
inline bool operator>=(const FileTime& lhs, const FileTime& rhs) noexcept
{
    return !(lhs < rhs);
}
#endif

class File
{
public:
    virtual void release() noexcept = 0;

    virtual omni::expected<void, ErrorCode> close() noexcept = 0;

    virtual omni::expected<size_t, ErrorCode> getSize() noexcept = 0;

    virtual omni::expected<FileTime, ErrorCode> getModTime() noexcept = 0;

    virtual omni::expected<FileTime, ErrorCode> getCreateTime() noexcept = 0;

    virtual omni::expected<size_t, ErrorCode> readChunk(void* buffer, size_t bytes) noexcept = 0;

    virtual omni::expected<size_t, ErrorCode> writeChunk(const void* buffer, size_t bytes) noexcept = 0;

    virtual omni::expected<omni::string, ErrorCode> readLine() noexcept = 0;

    virtual omni::expected<void, ErrorCode> readLine(omni::string& out) noexcept = 0;

    virtual omni::expected<void, ErrorCode> writeLine(cpp::string_view line) noexcept = 0;

    virtual omni::expected<void, ErrorCode> flush() noexcept = 0;

    virtual omni::expected<size_t, ErrorCode> getPosition() const noexcept = 0;

    virtual omni::expected<void, ErrorCode> setPosition(int64_t offset, FileWhence whence) noexcept = 0;

    virtual omni::expected<void, ErrorCode> truncate() noexcept = 0;

    virtual FileStatus getStatus() const noexcept = 0;

    bool isEof() const noexcept
    {
        return getStatus() == FileStatus::eEof;
    }

    bool isError() const noexcept
    {
        return getStatus() == FileStatus::eError;
    }

    virtual omni::expected<void, ErrorCode> getFileInfo(FileInfo& info) noexcept = 0;

    omni::expected<void, ErrorCode> resetPositionBegin() noexcept
    {
        return setPosition(0, FileWhence::eBegin);
    }

    omni::expected<void, ErrorCode> resetPositionEnd() noexcept
    {
        return setPosition(0, FileWhence::eEnd);
    }
};

namespace v2_detail
{
struct FileReleaser
{
    void operator()(File* f)
    {
        f->release();
    }
};
} // namespace v2_detail

using ScopedFile = std::unique_ptr<File, v2_detail::FileReleaser>;

struct IFileSystem
{
    CARB_PLUGIN_INTERFACE_EX("carb::filesystem::IFileSystem",
                             carb_filesystem_IFileSystem_latest,
                             carb_filesystem_IFileSystem)

    virtual cpp::zstring_view getExecutablePath() const noexcept = 0;

    virtual cpp::zstring_view getExecutableDirectoryPath() const noexcept = 0;

    virtual omni::expected<void, ErrorCode> setAppDirectoryPath(cpp::string_view path) noexcept = 0;

    virtual omni::string getAppDirectoryPath() const noexcept = 0;

    virtual omni::expected<void, ErrorCode> setWorkingDirectory(cpp::string_view path) const noexcept = 0;

    virtual omni::expected<omni::string, ErrorCode> getWorkingDirectory() const noexcept = 0;

    virtual omni::expected<bool, ErrorCode> checkExists(cpp::string_view path) const noexcept = 0;

    bool exists(cpp::string_view path) const noexcept
    {
        return checkExists(path).value_or(false);
    }

    virtual omni::expected<bool, ErrorCode> checkWritable(cpp::string_view path) const noexcept = 0;

    bool isWritable(cpp::string_view path) const noexcept
    {
        return checkWritable(path).value_or(false);
    }

    virtual omni::expected<bool, ErrorCode> checkReadable(cpp::string_view path) const noexcept = 0;

    bool isReadable(cpp::string_view path) const noexcept
    {
        return checkReadable(path).value_or(false);
    }

    virtual omni::expected<bool, ErrorCode> checkDirectory(cpp::string_view path) const noexcept = 0;

    bool isDirectory(cpp::string_view path) const noexcept
    {
        return checkDirectory(path).value_or(false);
    }

    virtual omni::expected<omni::string, ErrorCode> makeCanonicalPath(cpp::string_view path,
                                                                      bool mustExist = true) const noexcept = 0;

    virtual omni::expected<omni::string, ErrorCode> makeCanonicalPath(cpp::string_view path,
                                                                      cpp::string_view base,
                                                                      bool mustExist = true) const noexcept = 0;

    omni::expected<ScopedFile, ErrorCode> open(cpp::string_view path, OpenMode mode, bool binary) const noexcept
    {
        return internalOpen(path, mode, binary).transform([](File* in) { return ScopedFile(in); });
    }

    virtual omni::expected<FileTime, ErrorCode> getModTime(cpp::string_view path) const noexcept = 0;

    virtual omni::expected<FileTime, ErrorCode> getCreateTime(cpp::string_view path) const noexcept = 0;

    virtual FileTime getCurrentTime() const noexcept = 0;

    virtual omni::expected<void, ErrorCode> removeFile(cpp::string_view path) const noexcept = 0;

    virtual omni::expected<void, ErrorCode> removeDirectory(cpp::string_view path) const noexcept = 0;

    virtual omni::expected<omni::string, ErrorCode> makeTempDirectory() const noexcept = 0;

    virtual omni::expected<CreateType, ErrorCode> makeSingleDirectory(cpp::string_view path) const noexcept = 0;

    virtual omni::expected<CreateType, ErrorCode> makeDirectories(cpp::string_view path) const noexcept = 0;

    virtual omni::expected<void, ErrorCode> copyFile(cpp::string_view from, cpp::string_view to) const noexcept = 0;

    virtual omni::expected<void, ErrorCode> movePath(cpp::string_view from, cpp::string_view to) const noexcept = 0;

    template <typename Pred>
    omni::expected<void, ErrorCode> forEachDirectoryItem(cpp::string_view path, bool recurse, Pred&& pred) const;

    virtual omni::expected<SubscriptionId, ErrorCode> subscribeToChangeEvents(
        cpp::string_view path,
        omni::move_only_function<void(cpp::zstring_view, ChangeAction, cpp::zstring_view)> func) const noexcept = 0;

    virtual omni::expected<void, ErrorCode> unsubscribeFromChangeEvents(SubscriptionId id) const noexcept = 0;

    virtual omni::expected<void, ErrorCode> getFileInfo(cpp::string_view path, FileInfo& out) const noexcept = 0;

private:
    virtual omni::expected<File*, ErrorCode> internalOpen(cpp::string_view path,
                                                          OpenMode mode,
                                                          bool binary) const noexcept = 0;

    virtual omni::expected<void, ErrorCode> internalForEachDirectoryItem(cpp::string_view path,
                                                                         bool recurse,
                                                                         WalkAction (*callback)(const DirectoryItemInfo&,
                                                                                                void* userData),
                                                                         void* userData) const noexcept = 0;
};

template <typename Pred>
omni::expected<void, ErrorCode> IFileSystem::forEachDirectoryItem(cpp::string_view path, bool recurse, Pred&& pred) const
{
    using PredType = std::decay_t<Pred>;
    using RetType = cpp::invoke_result_t<Pred, const DirectoryItemInfo&>;
    if constexpr (std::is_void_v<RetType>)
    {
        return internalForEachDirectoryItem(path, recurse,
                                            +[](const DirectoryItemInfo& info, void* ud) {
                                                (*static_cast<PredType*>(ud))(info);
                                                return WalkAction::eContinue;
                                            },
                                            reinterpret_cast<void*>(&pred));
    }
    else
    {
        static_assert(std::is_same_v<WalkAction, RetType>, "Predicate must return WalkAction");
        return internalForEachDirectoryItem(
            path, recurse, +[](const DirectoryItemInfo& info, void* ud) { return (*static_cast<PredType*>(ud))(info); },
            reinterpret_cast<void*>(&pred));
    }
}

#ifndef DOXYGEN_BUILD
} // namespace v2
#endif
} // namespace carb::filesystem