view libirccd-js/irccd/js/file_jsapi.hpp @ 736:49b7c7660a00

Tests: use structured bindings in irccdctl
author David Demelier <markand@malikania.fr>
date Tue, 24 Jul 2018 22:14:00 +0200
parents 2838134d69bf
children
line wrap: on
line source

/*
 * file_jsapi.hpp -- Irccd.File API
 *
 * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#ifndef IRCCD_JS_FILE_JSAPI_HPP
#define IRCCD_JS_FILE_JSAPI_HPP

/**
 * \file file_jsapi.hpp
 * \brief Irccd.File Javascript API.
 */

#include <irccd/sysconfig.hpp>

#if defined(HAVE_STAT)
#   include <sys/types.h>
#   include <sys/stat.h>
#endif

#include <cassert>
#include <cerrno>
#include <cstdio>
#include <cstring>
#include <functional>
#include <stdexcept>
#include <string>

#include "jsapi.hpp"

namespace irccd {

/**
 * \brief Object for Javascript to perform I/O.
 *
 * This class can be constructed to Javascript.
 *
 * It is used in:
 *
 * - Irccd.File [constructor]
 * - Irccd.System.popen (optional)
 */
class file {
private:
    file(const file&) = delete;
    file& operator=(const file&) = delete;

    file(file&&) = delete;
    file& operator=(file&&) = delete;

private:
    std::string path_;
    std::FILE* stream_;
    std::function<void (std::FILE*)> destructor_;

public:
    /**
     * Construct a file specified by path
     *
     * \param path the path
     * \param mode the mode string (for std::fopen)
     * \throw std::runtime_error on failures
     */
    inline file(std::string path, const std::string& mode)
        : path_(std::move(path))
        , destructor_([] (std::FILE* fp) { std::fclose(fp); })
    {
        if ((stream_ = std::fopen(path_.c_str(), mode.c_str())) == nullptr)
            throw std::runtime_error(std::strerror(errno));
    }

    /**
     * Construct a file from a already created FILE pointer (e.g. popen).
     *
     * The class takes ownership of fp and will close it.
     *
     * \pre destructor must not be null
     * \param fp the file pointer
     * \param destructor the function to close fp (e.g. std::fclose)
     */
    inline file(std::FILE* fp, std::function<void (std::FILE*)> destructor) noexcept
        : stream_(fp)
        , destructor_(std::move(destructor))
    {
        assert(destructor_ != nullptr);
    }

    /**
     * Closes the file.
     */
    virtual ~file() noexcept
    {
        close();
    }

    /**
     * Get the path.
     *
     * \return the path
     * \warning empty when constructed from the FILE constructor
     */
    inline const std::string& get_path() const noexcept
    {
        return path_;
    }

    /**
     * Get the handle.
     *
     * \return the handle or nullptr if the stream was closed
     */
    inline std::FILE* get_handle() noexcept
    {
        return stream_;
    }

    /**
     * Force close, can be safely called multiple times.
     */
    inline void close() noexcept
    {
        if (stream_) {
            destructor_(stream_);
            stream_ = nullptr;
        }
    }
};

/**
 * \brief Irccd.File Javascript API.
 * \ingroup jsapi
 */
class file_jsapi : public jsapi {
public:
    /**
     * \copydoc jsapi::get_name
     */
    std::string get_name() const override;

    /**
     * \copydoc jsapi::load
     */
    void load(irccd& irccd, std::shared_ptr<js_plugin> plugin) override;
};

/**
 * \brief Specialization for generic file type as shared_ptr.
 *
 * Supports push, require.
 */
template <>
class dukx_type_traits<std::shared_ptr<file>> : public std::true_type {
public:
    /**
     * Push a file.
     *
     * \pre fp != nullptr
     * \param ctx the the context
     * \param fp the file
     */
    static void push(duk_context* ctx, std::shared_ptr<file> fp);

    /**
     * Require a file. Raises a JavaScript error if not a File.
     *
     * \param ctx the context
     * \param index the index
     * \return the file pointer
     */
    static std::shared_ptr<file> require(duk_context* ctx, duk_idx_t index);
};

#if defined(HAVE_STAT)

/**
 * \brief Specialization for struct stat.
 *
 * Supports push.
 */
template <>
class dukx_type_traits<struct stat> : public std::true_type {
public:
    /**
     * Push the stat information to the stack as Javascript object.
     *
     * \param ctx the context
     * \param st the stat structure
     */
    static void push(duk_context* ctx, const struct stat& st);
};

#endif // !HAVE_STAT

} // !irccd

#endif // !IRCCD_JS_FILE_JSAPI_HPP