//
// 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_CONVERSION_HPP
#define BOOST_JSON_CONVERSION_HPP

#include <boost/json/detail/config.hpp>

#include <type_traits>

namespace boost {
namespace json {

/** Customization point tag.

    This tag type is used by the function
    @ref value_from to select overloads
    of `tag_invoke`.

    @note This type is empty; it has no members.

    @see @ref value_from, @ref value_to, @ref value_to_tag,
    <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1895r0.pdf">
        tag_invoke: A general pattern for supporting customisable functions</a>
*/
struct value_from_tag { };

/** Customization point tag type.

    This tag type is used by the function
    @ref value_to to select overloads
    of `tag_invoke`.

    @note This type is empty; it has no members.

    @see @ref value_from, @ref value_from_tag, @ref value_to,
    <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1895r0.pdf">
        tag_invoke: A general pattern for supporting customisable functions</a>
*/
template<class T>
struct value_to_tag { };

/** Customization point tag type.

    This tag type is used by the function
    @ref try_value_to to select overloads
    of `tag_invoke`.

    @note This type is empty; it has no members.

    @see @ref value_to, @ref value_to_tag
    <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1895r0.pdf">
        tag_invoke: A general pattern for supporting customisable functions</a>
*/
template<class T>
struct try_value_to_tag { };

/** Determine if `T` can be treated like a string during conversions.

    Provides the member constant `value` that is equal to `true`, if `T` is
    convertible to @ref string_view. Otherwise, `value` is equal to `false`.
    <br>

    Users can specialize the trait for their own types if they don't want them
    to be treated like strings. For example:

    @code
    namespace boost {
    namespace json {

    template <>
    struct is_string_like<your::string> : std::false_type
    { };

    } // namespace boost
    } // namespace json
    @endcode

    @par Types satisfying the trait

    @ref string,
    @ref string_view,
    <a href="https://en.cppreference.com/w/cpp/string/basic_string"><tt>std::string</tt></a>,
    <a href="https://en.cppreference.com/w/cpp/string/basic_string_view"><tt>std::string_view</tt></a>.

    @see @ref value_from, @ref value_to
*/
template<class T>
struct is_string_like;

/** Determine if `T` can be treated like a sequence during conversions.

    Given `t`, a glvalue of type `T`, if

    @li given `It`, the type denoted by `decltype(std::begin(t))`,
        <tt>std::iterator_traits<It>::iterator_category</tt> is well-formed and
        denotes a type; and

    @li `decltype(std::end(t))` also denotes the type `It`;

    then the trait provides the member constant `value` that is equal to
    `true`. Otherwise, `value` is equal to `false`.<br>

    Users can specialize the trait for their own types if they don't want them
    to be treated like sequences. For example:

    @code
    namespace boost {
    namespace json {

    template <>
    struct is_sequence_like<your::container> : std::false_type
    { };

    } // namespace boost
    } // namespace json
    @endcode


    @par Types satisfying the trait

    Any <a href="https://en.cppreference.com/w/cpp/named_req/SequenceContainer"><em>SequenceContainer</em></a>,
    array types.

    @see @ref value_from, @ref value_to
*/
template<class T>
struct is_sequence_like;

/** Determine if `T` can be treated like a 1-to-1 mapping during
    conversions.

    Given `t`, a glvalue of type `T`, if

    @li <tt>is_sequence_like<T>::value</tt> is `true`; and

    @li given type `It` denoting `decltype(std::begin(t))`, and types `K`
        and `M`,  <tt>std::iterator_traits<It>::value_type</tt> denotes
        `std::pair<K, M>`; and

    @li <tt>std::is_string_like<K>::value</tt> is `true`; and

    @li given `v`, a glvalue of type `V`, and `E`, the type denoted by
        `decltype(t.emplace(v))`,
        <tt>std::is_tuple_like<E>::value</tt> is `true`;

    then the trait provides the member constant `value`
    that is equal to `true`. Otherwise, `value` is equal to `false`.<br>

    Users can specialize the trait for their own types if they don't want them
    to be treated like mappings. For example:

    @code
    namespace boost {
    namespace json {

    template <>
    struct is_map_like<your::map> : std::false_type
    { };

    } // namespace boost
    } // namespace json
    @endcode


    @note

    The restriction for `t.emplace()` return type ensures that the container
    does not accept duplicate keys.

    @par Types satisfying the trait

    <a href="https://en.cppreference.com/w/cpp/container/map"><tt>std::map</tt></a>,
    <a href="https://en.cppreference.com/w/cpp/container/unordered_map"><tt>std::unordered_map</tt></a>.

    @see @ref value_from, @ref value_to
*/
template<class T>
struct is_map_like;

/** Determine if `T` can be treated like a tuple during conversions.

    Provides the member constant `value` that is equal to `true`, if
    <tt>std::tuple_size<T>::value</tt> is a positive number. Otherwise, `value`
    is equal to `false`.<br>

    Users can specialize the trait for their own types if they don't want them
    to be treated like tuples. For example:

    @code
    namespace boost {
    namespace json {

    template <>
    struct is_tuple_like<your::tuple> : std::false_type
    { };

    } // namespace boost
    } // namespace json
    @endcode


    @par Types satisfying the trait

    <a href="https://en.cppreference.com/w/cpp/utility/tuple"><tt>std::tuple</tt></a>,
    <a href="https://en.cppreference.com/w/cpp/utility/pair"><tt>std::pair</tt></a>.

    @see @ref value_from, @ref value_to
*/
template<class T>
struct is_tuple_like;

/** Determine if `T` can be treated like null during conversions.

    Primary template instantiations provide the member constant `value` that is
    equal to `false`. Users can specialize the trait for their own types if
    they **do** want them to be treated as nulls. For example:

    @code
    namespace boost {
    namespace json {

    template <>
    struct is_null_like<your::null_type> : std::true_type
    { };

    } // namespace boost
    } // namespace json
    @endcode


    @par Types satisfying the trait

    <a href="https://en.cppreference.com/w/cpp/types/nullptr_t"><tt>std::nullptr_t</tt></a>.

    @see @ref value_from, @ref value_to
*/
template<class T>
struct is_null_like
    : std::false_type
{ };

/** Determine if `T` should be treated as a described class

    Described classes are serialised as objects with an element for each
    described public data member. A described class should not have described
    bases or non-public members.<br>

    Or more formally, given `L`, a class template
    of the form `template<class...> struct L {};`, if

    @li <tt>boost::describe::has_members<T, boost::describe::mod_public>::value</tt> is `true`; and

    @li `boost::describe::describe_members<T, boost::describe::mod_private | boost::describe::mod_protected>` denotes `L<>`; and

    @li `boost::describe::describe_bases<T, boost::describe::mod_any_access>` denotes `L<>`; and

    @li <tt>std::is_union<T>::value</tt> is `false`;

    then the trait provides the member constant `value`
    that is equal to `true`. Otherwise, `value` is equal to `false`.<br>

    Users can specialize the trait for their own types if they don't want them
    to be treated as described classes. For example:

    @code
    namespace boost {
    namespace json {

    template <>
    struct is_described_class<your::described_class> : std::false_type
    { };

    } // namespace boost
    } // namespace json
    @endcode

    Users can also specialize the trait for their own types _with_ described
    bases to enable this conversion implementation. In this case the class will
    be serialized in a flattened way, that is members of bases will be
    serialized as direct elements of the object, and no nested objects will be
    created for bases.

    @see <a href="https://www.boost.org/doc/libs/develop/libs/describe/doc/html/describe.html">Boost.Describe</a>.
*/
template<class T>
struct is_described_class;

/** Determine if `T` should be treated as a described enum

    Described enums are serialised as strings when their value equals to a
    described enumerator, and as integers otherwise. The reverse operation
    does not convert numbers to enums values, though, and instead produces
    an error.<br>

    If <tt>boost::describe::has_describe_enumerators<T>::value</tt> is `true`,
    then the trait provides the member constant `value`
    that is equal to `true`. Otherwise, `value` is equal to `false`.<br>

    Users can specialize the trait for their own enums if they don't want them
    to be treated as described enums. For example:

    @code
    namespace boost {
    namespace json {

    template <>
    struct is_described_enum<your::described_enum> : std::false_type
    { };

    } // namespace boost
    } // namespace json
    @endcode

    @see <a href="https://www.boost.org/doc/libs/develop/libs/describe/doc/html/describe.html">Boost.Describe</a>.
*/
template<class T>
struct is_described_enum;

} // namespace json
} // namespace boost

#include <boost/json/impl/conversion.hpp>

#endif // BOOST_JSON_CONVERSION_HPP
