/*
 * Copyright 2019 Bloomberg Finance LP
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef INCLUDED_BUILDBOXCOMMON_FILESTAGER_H
#define INCLUDED_BUILDBOXCOMMON_FILESTAGER_H

#include <buildboxcommon_localcas.h>
#include <buildboxcommon_systemutils.h>

#include <optional>
#include <unistd.h>

namespace buildboxcommon {

typedef std::optional<
    std::function<void(struct stat st, const Digest &digest)>>
    FileStagedCallback;

class FileStager {
    /* Allows staging the contents of directories stored in a LocalCAS
     * instance.
     *
     * Stagers will always need read access to the storage where the blobs are
     * located.
     */
  public:
    inline explicit FileStager(LocalCas *cas_storage)
        : d_cas_storage(cas_storage)
    {
    }

    virtual ~FileStager() = default;

    /*
     * Represents a directory that is currently staged.
     *
     * Its lifetime will determine how long does the directory remain staged.
     * (The destructor will invoke the necessary steps to unstage it).
     */
    class StagedDirectory {
      public:
        StagedDirectory() = default;

        virtual ~StagedDirectory() = default;

        // Copies are disallowed because ownership should be exclusive to the
        // owner of the staged directory.
        StagedDirectory(const StagedDirectory &) = delete;
        StagedDirectory &operator=(StagedDirectory const &) = delete;

        // Disable move and move assignment operators
        StagedDirectory(StagedDirectory &&) = delete;
        StagedDirectory &operator=(StagedDirectory &&) = delete;
    };

    /*
     * Stage a directory in the given path.
     *
     * (Particular staging implementations might make that path an optional
     * argument.)
     */
    virtual std::unique_ptr<StagedDirectory>
    stage(const Digest &root_digest, const std::string &path,
          const std::optional<ProcessCredentials> &accessCredentials =
              std::nullopt,
          const FileStagedCallback &callback = std::nullopt) = 0;

    virtual std::string getHashXattrName(int /*dirfd*/) { return ""; }

  protected:
    // NOLINTBEGIN (cppcoreguidelines-non-private-member-variables-in-classes)
    LocalCas *d_cas_storage;
    // NOLINTEND (cppcoreguidelines-non-private-member-variables-in-classes)
};
} // namespace buildboxcommon

#endif // INCLUDED_BUILDBOXCOMMON_FILESTAGER_H
