/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * Copyright 2010-2018
 *     Danny Robson <danny@nerdcruft.net>
 */

#pragma once

#include <functional>
#include <stdexcept>

namespace cruft::posix {
    /// An exception class used for reporting errors signalled by errno.
    ///
    /// Ideally this would be named `errno' but that symbol is permitted to
    /// be a macro and significantly complicates symbol resolution either way.
    class error : public std::exception {
    public:
        explicit error (int code);
        error ();

        int code (void) const;
        static int last_code (void);

        template <typename FunctionT, typename ...Args>
        static auto
        try_call (FunctionT &&func, Args&& ...args)
        {
            return try_value (
                std::invoke (
                    std::forward<FunctionT> (func),
                    std::forward<Args> (args)...
                )
            );
        }

        static void try_code (void);
        static void try_code (int code);

        static void throw_code [[gnu::noreturn]] (void);
        static void throw_code [[gnu::noreturn]] (int code);

        virtual const char* what (void) const noexcept final override;

        template <typename T>
        static T try_value (T value)
        {
            if (value < 0)
                throw_code ();
            return value;
        }

    private:
        int const m_code;
    };


    template <int CodeV>
    struct error_code : public error {
        error_code ():
            error (CodeV)
        { ; }
    };



    class eai : public std::runtime_error {
    public:
        explicit eai (int code);
        int code (void) const;

        static void try_code (int);
        static void throw_code [[gnu::noreturn]] (int);

    private:
        int m_code;
    };
};