//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
// Copyright (c) 2022 Dmitry Arkhipov (grisumbras@yandex.ru)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//

#ifndef BOOST_JSON_SYSTEM_ERROR_HPP
#define BOOST_JSON_SYSTEM_ERROR_HPP

#include <boost/json/detail/config.hpp>
#include <boost/json/fwd.hpp>
#include <boost/assert/source_location.hpp>
#include <boost/system/error_code.hpp>
#include <boost/system/result.hpp>
#include <boost/system/system_error.hpp>

namespace boost {
namespace json {

/// The type of error code used by the library.
using error_code = boost::system::error_code;

/// The type of error category used by the library.
using error_category = boost::system::error_category;

/// The type of error condition used by the library.
using error_condition = boost::system::error_condition;

/// The type of system error thrown by the library.
using system_error = boost::system::system_error;

/** The type of result returned by library functions

    This is an alias template used as the return type for functions that can
    either return a value, or fail with an error code. This is a brief
    synopsis of the type:

    @par Declaration
    @code
    template< class T >
    class result
    {
    public:
        // Return true if the result contains an error
        constexpr bool has_error() const noexcept;

        // These two return true if the result contains a value
        constexpr bool has_value() const noexcept;
        constexpr explicit operator bool() const noexcept;

        // Return the value or throw an exception if has_value() == false
        constexpr T& value();
        constexpr T const& value() const;

        // Return the value, assuming the result contains it
        constexpr T& operator*();
        constexpr T const& operator*() const;

        // Return the error, which is default constructed if has_error() == false
        constexpr error_code error() const noexcept;
        ...more
    };
    @endcode

    @par Usage
    Given the function @ref try_value_to with this signature:

    @code
    template< class T>
    result< T > try_value_to( const value& jv );
    @endcode

    The following statement captures the value in a variable upon success,
    otherwise throws:
    @code
    int n = try_value_to<int>( jv ).value();
    @endcode

    This statement captures the result in a variable and inspects the error
    condition:
    @code
    result< int > r = try_value_to<int>( jv );
    if( r )
        std::cout << *r;
    else
        std::cout << r.error();
    @endcode

    @note For a full synopsis of the type, please see the corresponding
    documentation in Boost.System.

    @tparam T The type of value held by the result.
*/
template< class T >
using result = boost::system::result<T>;

/**
   Helper trait that returns @ref result

   The primary template is an incomplete type. The library provides a partial
   specialisation `result_for<T1, value>`, that has nested type alias `type`
   that aliases the type `result<T1>`.

   The purpose of this trait is to let users provide non-throwing conversions
   for their types without creating a physical dependency on Boost.Json. For
   example:

   @code
   namespace boost
   {
   namespace json
   {

   template<class T>
   struct value_to_tag;

   template<class T1, class T2>
   struct result_for;
   }
   }

   namespace mine
   {
       class my_class;
       ...
       template<class JsonValue>
       boost::json::result_for<my_class, JsonValue>
       tag_invoke(boost::json::try_value_to_tag<my_class>, const JsonValue& jv)
       { ... }
   }
   @endcode

    @see @ref try_value_to, @ref try_value_to_tag
*/
template <class T1, class T2>
struct result_for;

/** Create @ref result storing a portable error code

    This function constructs a `result<T>` that stores @ref error_code with
    `value()` equal to `e` and `category()` equal to
    `boost::system::generic_category()`. <br>

    The main use for this function is in implementation of functions returning
    @ref result, without including `boost/json/system_error.hpp` or even
    `<system_error>`. In particular, it may be useful for customizations of
    @ref try_value_to without creating a physical dependency on Boost.JSON.
    For example:

    @code
    #include <cerrno>
    #include <boost/assert/source_location.hpp>

    namespace boost
    {
    namespace json
    {

    class value;

    template<class T>
    struct try_value_to_tag;

    template<class T1, class T2>
    struct result_for;

    template <class T>
    typename result_for<T, value>::type
    result_from_errno(int e, boost::source_location const* loc) noexcept

    }
    }

    namespace mine
    {

    class my_class;
    ...
    template<class JsonValue>
    boost::json::result_for<my_class, JsonValue>
    tag_invoke(boost::json::try_value_to_tag<my_class>, const JsonValue& jv)
    {
        BOOST_STATIC_CONSTEXPR boost::source_location loc = BOOST_CURRENT_LOCATION;
        if( !jv.is_null() )
            return boost::json::result_from_errno<my_class>(EINVAL, &loc);
        return my_class();
    }

    }
    @endcode

    @par Exception Safety
    Does not throw exceptions.

    @tparam T The value type of returned `result`.

    @param e The error value.

    @param loc The error location.

    @returns @ref error_code with `value()` equal to `e` and `category()` equal
    to `boost::system::generic_category()`.

    @see @ref try_value_to_tag, @ref try_value_to, @ref result_for,
    <a href="https://www.boost.org/doc/libs/develop/libs/system/doc/html/system.html#ref_generic_category">
        `boost::system::generic_category`</a>,
    <a href="https://www.boost.org/doc/libs/master/libs/assert/doc/html/assert.html#source_location_support">
        `boost::source_location`</a>.
*/
template <class T>
typename result_for<T, value>::type
result_from_errno(int e, boost::source_location const* loc) noexcept
{
    error_code ec(e, system::generic_category(), loc);
    return {system::in_place_error, ec};
}

} // namespace json
} // namespace boost

#endif
