//
// Copyright (c) 2019-2023 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
//
// 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)
//

#ifndef BOOST_MYSQL_FIELD_HPP
#define BOOST_MYSQL_FIELD_HPP

#include <boost/mysql/blob.hpp>
#include <boost/mysql/field_kind.hpp>
#include <boost/mysql/field_view.hpp>
#include <boost/mysql/string_view.hpp>

#include <boost/mysql/detail/auxiliar/field_impl.hpp>

#include <boost/variant2/variant.hpp>

#include <cstddef>
#include <ostream>
#include <string>
#ifdef __cpp_lib_string_view
#include <string_view>
#endif

namespace boost {
namespace mysql {

/**
 * \brief Variant-like class that can represent of any of the allowed database types.
 * \details
 * This is a regular variant-like class that can represent any of the types that MySQL allows. It
 * has value semantics (as opposed to \ref field_view). Instances of this class are not created
 * by the library. They should be created by the user, when the reference semantics of
 * \ref field_view are not appropriate.
 * \n
 * Like a variant, at any point, a `field` always contains a value of
 * certain type. You can query the type using \ref kind and the `is_xxx` functions
 * like \ref is_int64. Use `as_xxx` and `get_xxx` for checked and unchecked value
 * access, respectively. You can mutate a `field` by calling the assignment operator,
 * or using the lvalue references returned by `as_xxx` and `get_xxx`.
 */
class field
{
public:
    /**
     * \brief Constructs a `field` holding NULL.
     * \par Exception safety
     * No-throw guarantee.
     */
    field() = default;

    /**
     * \brief Copy constructor.
     * \par Exception safety
     * Strong guarantee. Internal allocations may throw.
     */
    field(const field&) = default;

    /**
     * \brief Move constructor.
     * \par Exception safety
     * No-throw guarantee.
     *
     * \par Object lifetimes
     * All references into `other` are invalidated, including the ones obtained by calling
     * get_xxx, as_xxx and \ref field::operator field_view().
     */
    field(field&& other) = default;

    /**
     * \brief Copy assignment.
     * \par Exception safety
     * Basic guarantee. Internal allocations may throw.
     *
     * \par Object lifetimes
     * Invalidates references obtained by as_xxx and get_xxx functions,
     * but not the ones obtained by \ref field::operator field_view().
     */
    field& operator=(const field&) = default;

    /**
     * \brief Move assignment.
     * \par Exception safety
     * No-throw guarantee.
     *
     * \par Object lifetimes
     * Invalidates references to `*this` obtained by as_xxx and get_xxx functions,
     * but not the ones obtained by \ref field::operator field_view(). All references into `other`
     * are invalidated, including the ones obtained by calling get_xxx, as_xxx and
     * \ref field::operator field_view().
     */
    field& operator=(field&& other) = default;

    /// Destructor.
    ~field() = default;

    /**
     * \brief Constructs a `field` holding NULL.
     * \details
     * Caution: `field(NULL)` will __NOT__ match this overload. It will try to construct
     * a `string_view` from a NULL C string, causing undefined behavior.
     *
     * \par Exception safety
     * No-throw guarantee.
     */
    explicit field(std::nullptr_t) noexcept {}

    /**
     * \brief Constructs a `field` holding an `int64`.
     * \par Exception safety
     * No-throw guarantee.
     */
    explicit field(signed char v) noexcept : repr_(std::int64_t(v)) {}

    /// \copydoc field(signed char)
    explicit field(short v) noexcept : repr_(std::int64_t(v)) {}

    /// \copydoc field(signed char)
    explicit field(int v) noexcept : repr_(std::int64_t(v)) {}

    /// \copydoc field(signed char)
    explicit field(long v) noexcept : repr_(std::int64_t(v)) {}

    /// \copydoc field(signed char)
    explicit field(long long v) noexcept : repr_(std::int64_t(v)) {}

    /**
     * \brief Constructs a `field` holding an `uint64`.
     * \par Exception safety
     * No-throw guarantee.
     */
    explicit field(unsigned char v) noexcept : repr_(std::uint64_t(v)) {}

    /// \copydoc field(unsigned char)
    explicit field(unsigned short v) noexcept : repr_(std::uint64_t(v)) {}

    /// \copydoc field(unsigned char)
    explicit field(unsigned int v) noexcept : repr_(std::uint64_t(v)) {}

    /// \copydoc field(unsigned char)
    explicit field(unsigned long v) noexcept : repr_(std::uint64_t(v)) {}

    /// \copydoc field(unsigned char)
    explicit field(unsigned long long v) noexcept : repr_(std::uint64_t(v)) {}

    /**
     * \brief Constructs a `field` holding a string.
     * \par Exception safety
     * Strong guarantee. Internal allocations may throw.
     */
    explicit field(const std::string& v) : repr_(v) {}

    /**
     * \brief Constructs a `field` holding a string.
     * \details v is moved into an internal `std::string` object.
     * \par Exception safety
     * No-throw guarantee.
     */
    explicit field(std::string&& v) noexcept : repr_(std::move(v)) {}

    /// \copydoc field(const std::string&)
    explicit field(const char* v) : repr_(boost::variant2::in_place_type_t<std::string>(), v) {}

    /// \copydoc field(const std::string&)
    explicit field(string_view v) : repr_(boost::variant2::in_place_type_t<std::string>(), v) {}

#if defined(__cpp_lib_string_view) || defined(BOOST_MYSQL_DOXYGEN)
    /// \copydoc field(const std::string&)
    explicit field(std::string_view v) noexcept : repr_(boost::variant2::in_place_type_t<std::string>(), v) {}
#endif

    /**
     * \brief Constructs a `field` holding a `blob`.
     * \details v is moved into an internal `blob` object.
     * \par Exception safety
     * No-throw guarantee.
     */
    explicit field(blob v) noexcept : repr_(std::move(v)) {}

    /**
     * \brief Constructs a `field` holding a `float`.
     * \par Exception safety
     * No-throw guarantee.
     */
    explicit field(float v) noexcept : repr_(v) {}

    /**
     * \brief Constructs a `field` holding a `double`.
     * \par Exception safety
     * No-throw guarantee.
     */
    explicit field(double v) noexcept : repr_(v) {}

    /**
     * \brief Constructs a `field` holding a `date`.
     * \par Exception safety
     * No-throw guarantee.
     */
    explicit field(const date& v) noexcept : repr_(v) {}

    /**
     * \brief Constructs a `field` holding a `datetime`.
     * \par Exception safety
     * No-throw guarantee.
     */
    explicit field(const datetime& v) noexcept : repr_(v) {}

    /**
     * \brief Constructs a `field` holding a `time`.
     * \par Exception safety
     * No-throw guarantee.
     */
    explicit field(const time& v) noexcept : repr_(v) {}

    /**
     * \brief Constructs a `field` from a \ref field_view.
     * \details The resulting `field` has the same kind and value as the original `field_view`.
     *
     * \par Exception safety
     * Strong guarantee. Internal allocations may throw.
     *
     * \par Object lifetimes
     * The resulting `field` is guaranteed to be valid even after `v` becomes invalid.
     */
    field(const field_view& v) { from_view(v); }

    /**
     * \brief Replaces `*this` with a `NULL`, changing the kind to `null` and destroying any
     * previous contents.
     *
     * \par Exception safety
     * No-throw guarantee.
     *
     * \par Object lifetimes
     * Invalidates references obtained by as_xxx and get_xxx functions,
     * but not the ones obtained by \ref field::operator field_view().
     */
    field& operator=(std::nullptr_t) noexcept
    {
        repr_.data.emplace<detail::field_impl::null_t>();
        return *this;
    }

    /**
     * \brief Replaces `*this` with `v`, changing the kind to `int64` and destroying any
     * previous contents.
     *
     * \par Exception safety
     * No-throw guarantee.
     *
     * \par Object lifetimes
     * Invalidates references obtained by as_xxx and get_xxx functions,
     * but not the ones obtained by \ref field::operator field_view().
     */
    field& operator=(signed char v) noexcept
    {
        repr_.data.emplace<std::int64_t>(v);
        return *this;
    }

    /// \copydoc operator=(signed char)
    field& operator=(short v) noexcept
    {
        repr_.data.emplace<std::int64_t>(v);
        return *this;
    }

    /// \copydoc operator=(signed char)
    field& operator=(int v) noexcept
    {
        repr_.data.emplace<std::int64_t>(v);
        return *this;
    }

    /// \copydoc operator=(signed char)
    field& operator=(long v) noexcept
    {
        repr_.data.emplace<std::int64_t>(v);
        return *this;
    }

    /// \copydoc operator=(signed char)
    field& operator=(long long v) noexcept
    {
        repr_.data.emplace<std::int64_t>(v);
        return *this;
    }

    /**
     * \brief Replaces `*this` with `v`, changing the kind to `uint64` and destroying any
     * previous contents.
     *
     * \par Exception safety
     * No-throw guarantee.
     *
     * \par Object lifetimes
     * Invalidates references obtained by as_xxx and get_xxx functions,
     * but not the ones obtained by \ref field::operator field_view().
     */
    field& operator=(unsigned char v) noexcept
    {
        repr_.data.emplace<std::uint64_t>(v);
        return *this;
    }

    /// \copydoc operator=(unsigned char)
    field& operator=(unsigned short v) noexcept
    {
        repr_.data.emplace<std::uint64_t>(v);
        return *this;
    }

    /// \copydoc operator=(unsigned char)
    field& operator=(unsigned int v) noexcept
    {
        repr_.data.emplace<std::uint64_t>(v);
        return *this;
    }

    /// \copydoc operator=(unsigned char)
    field& operator=(unsigned long v) noexcept
    {
        repr_.data.emplace<std::uint64_t>(v);
        return *this;
    }

    /// \copydoc operator=(unsigned char)
    field& operator=(unsigned long long v) noexcept
    {
        repr_.data.emplace<std::uint64_t>(v);
        return *this;
    }

    /**
     * \brief Replaces `*this` with `v`, changing the kind to `string` and destroying any previous
     * contents.
     *
     * \par Exception safety
     * Basic guarantee. Internal allocations may throw.
     *
     * \par Object lifetimes
     * Invalidates references obtained by as_xxx and get_xxx functions,
     * but not the ones obtained by \ref field::operator field_view().
     */
    field& operator=(const std::string& v)
    {
        repr_.data.emplace<std::string>(v);
        return *this;
    }

    /// \copydoc operator=(const std::string&)
    field& operator=(std::string&& v)
    {
        repr_.data.emplace<std::string>(std::move(v));
        return *this;
    }

    /// \copydoc operator=(const std::string&)
    field& operator=(const char* v)
    {
        repr_.data.emplace<std::string>(v);
        return *this;
    }

    /// \copydoc operator=(const std::string&)
    field& operator=(string_view v)
    {
        repr_.data.emplace<std::string>(v);
        return *this;
    }

#if defined(__cpp_lib_string_view) || defined(BOOST_MYSQL_DOXYGEN)
    /// \copydoc operator=(const std::string&)
    field& operator=(std::string_view v)
    {
        repr_.data.emplace<std::string>(v);
        return *this;
    }
#endif

    /**
     * \brief Replaces `*this` with `v`, changing the kind to `blob` and destroying any
     * previous contents.
     *
     * \par Exception safety
     * Basic guarantee. Internal allocations may throw.
     *
     * \par Object lifetimes
     * Invalidates references obtained by as_xxx and get_xxx functions,
     * but not the ones obtained by \ref field::operator field_view().
     */
    field& operator=(blob v)
    {
        repr_.data.emplace<blob>(std::move(v));
        return *this;
    }

    /**
     * \brief Replaces `*this` with `v`, changing the kind to `float_` and destroying any
     * previous contents.
     *
     * \par Exception safety
     * No-throw guarantee.
     *
     * \par Object lifetimes
     * Invalidates references obtained by as_xxx and get_xxx functions,
     * but not the ones obtained by \ref field::operator field_view().
     */
    field& operator=(float v) noexcept
    {
        repr_.data.emplace<float>(v);
        return *this;
    }

    /**
     * \brief Replaces `*this` with `v`, changing the kind to `double` and destroying any
     * previous contents.
     *
     * \par Exception safety
     * No-throw guarantee.
     *
     * \par Object lifetimes
     * Invalidates references obtained by as_xxx and get_xxx functions,
     * but not the ones obtained by \ref field::operator field_view().
     */
    field& operator=(double v) noexcept
    {
        repr_.data.emplace<double>(v);
        return *this;
    }

    /**
     * \brief Replaces `*this` with `v`, changing the kind to `date` and destroying any
     * previous contents.
     *
     * \par Exception safety
     * No-throw guarantee.
     *
     * \par Object lifetimes
     * Invalidates references obtained by as_xxx and get_xxx functions,
     * but not the ones obtained by \ref field::operator field_view().
     */
    field& operator=(const date& v) noexcept
    {
        repr_.data.emplace<date>(v);
        return *this;
    }

    /**
     * \brief Replaces `*this` with `v`, changing the kind to `datetime` and destroying any
     * previous contents.
     *
     * \par Exception safety
     * No-throw guarantee.
     *
     * \par Object lifetimes
     * Invalidates references obtained by as_xxx and get_xxx functions,
     * but not the ones obtained by \ref field::operator field_view().
     */
    field& operator=(const datetime& v) noexcept
    {
        repr_.data.emplace<datetime>(v);
        return *this;
    }

    /**
     * \brief Replaces `*this` with `v`, changing the kind to `time` and destroying any
     * previous contents.
     *
     * \par Exception safety
     * No-throw guarantee.
     *
     * \par Object lifetimes
     * Invalidates references obtained by as_xxx and get_xxx functions, but not
     */
    field& operator=(const time& v) noexcept
    {
        repr_.data.emplace<time>(v);
        return *this;
    }

    /**
     * \brief Replaces `*this` with `v`, changing the kind to `v.kind()` and destroying any previous
     * contents.
     *
     * \par Exception safety
     * Basic guarantee. Internal allocations may throw.
     *
     * \par Object lifetimes
     * Invalidates references to `*this` obtained by as_xxx and get_xxx functions, but not
     * the ones obtained by \ref field::operator field_view().
     *\n
     * `*this` is guaranteed to be valid even after `v` becomes invalid.
     */
    field& operator=(const field_view& v)
    {
        from_view(v);
        return *this;
    }

    /**
     * \brief Returns the type of the value this `field` is holding.
     * \par Exception safety
     * No-throw guarantee.
     */
    field_kind kind() const noexcept { return repr_.kind(); }

    /**
     * \brief Returns whether this `field` is holding a `NULL` value.
     * \par Exception safety
     * No-throw guarantee.
     */
    bool is_null() const noexcept { return kind() == field_kind::null; }

    /**
     * \brief Returns whether this `field` is holding a `int64` value.
     * \par Exception safety
     * No-throw guarantee.
     */
    bool is_int64() const noexcept { return kind() == field_kind::int64; }

    /**
     * \brief Returns whether this `field` is holding a `uint64` value.
     * \par Exception safety
     * No-throw guarantee.
     */
    bool is_uint64() const noexcept { return kind() == field_kind::uint64; }

    /**
     * \brief Returns whether this `field` is holding a string value.
     * \par Exception safety
     * No-throw guarantee.
     */
    bool is_string() const noexcept { return kind() == field_kind::string; }

    /**
     * \brief Returns whether this `field` is holding a blob value.
     * \par Exception safety
     * No-throw guarantee.
     */
    bool is_blob() const noexcept { return kind() == field_kind::blob; }

    /**
     * \brief Returns whether this `field` is holding a `float` value.
     * \par Exception safety
     * No-throw guarantee.
     */
    bool is_float() const noexcept { return kind() == field_kind::float_; }

    /**
     * \brief Returns whether this `field` is holding a `double` value.
     * \par Exception safety
     * No-throw guarantee.
     */
    bool is_double() const noexcept { return kind() == field_kind::double_; }

    /**
     * \brief Returns whether this `field` is holding a `date` value.
     * \par Exception safety
     * No-throw guarantee.
     */
    bool is_date() const noexcept { return kind() == field_kind::date; }

    /**
     * \brief Returns whether this `field` is holding a `datetime` value.
     * \par Exception safety
     * No-throw guarantee.
     */
    bool is_datetime() const noexcept { return kind() == field_kind::datetime; }

    /**
     * \brief Returns whether this `field` is holding a `time` value.
     * \par Exception safety
     * No-throw guarantee.
     */
    bool is_time() const noexcept { return kind() == field_kind::time; }

    /**
     * \brief Retrieves a reference to the underlying `std::int64_t` value or throws an exception.
     * \par Exception safety
     * Strong guarantee. Throws on type mismatch.
     * \throws bad_field_access If `!this->is_int64()`
     *
     * \par Object lifetimes
     * The returned reference is valid as long as `*this` is alive and no function that invalidates
     * references is called on `*this`.
     */
    const std::int64_t& as_int64() const { return repr_.as<std::int64_t>(); }

    /**
     * \brief Retrieves a reference to the underlying `std::uint64_t` value or throws an exception.
     * \par Exception safety
     * Strong guarantee. Throws on type mismatch.
     * \throws bad_field_access If `!this->is_uint64()`
     *
     * \par Object lifetimes
     * The returned reference is valid as long as `*this` is alive and no function that invalidates
     * references is called on `*this`.
     */
    const std::uint64_t& as_uint64() const { return repr_.as<std::uint64_t>(); }

    /**
     * \brief Retrieves a reference to the underlying `std::string` value or throws an exception.
     * \par Exception safety
     * Strong guarantee. Throws on type mismatch.
     * \throws bad_field_access If `!this->is_string()`
     *
     * \par Object lifetimes
     * The returned reference is valid as long as `*this` is alive and no function that invalidates
     * references is called on `*this`.
     */
    const std::string& as_string() const { return repr_.as<std::string>(); }

    /**
     * \brief Retrieves a reference to the underlying `blob` value or throws an exception.
     * \par Exception safety
     * Strong guarantee. Throws on type mismatch.
     * \throws bad_field_access If `!this->is_blob()`
     *
     * \par Object lifetimes
     * The returned reference is valid as long as `*this` is alive and no function that invalidates
     * references is called on `*this`.
     */
    const blob& as_blob() const { return repr_.as<blob>(); }

    /**
     * \brief Retrieves a reference to the underlying `float` value or throws an exception.
     * \par Exception safety
     * Strong guarantee. Throws on type mismatch.
     * \throws bad_field_access If `!this->is_float()`
     *
     * \par Object lifetimes
     * The returned reference is valid as long as `*this` is alive and no function that invalidates
     * references is called on `*this`.
     */
    const float& as_float() const { return repr_.as<float>(); }

    /**
     * \brief Retrieves a reference to the underlying `double` value or throws an exception.
     * \par Exception safety
     * Strong guarantee. Throws on type mismatch.
     * \throws bad_field_access If `!this->is_double()`
     *
     * \par Object lifetimes
     * The returned reference is valid as long as `*this` is alive and no function that invalidates
     * references is called on `*this`.
     */
    const double& as_double() const { return repr_.as<double>(); }

    /**
     * \brief Retrieves a reference to the underlying `date` value or throws an exception.
     * \par Exception safety
     * Strong guarantee. Throws on type mismatch.
     * \throws bad_field_access If `!this->is_date()`
     *
     * \par Object lifetimes
     * The returned reference is valid as long as `*this` is alive and no function that invalidates
     * references is called on `*this`.
     */
    const date& as_date() const { return repr_.as<date>(); }

    /**
     * \brief Retrieves a reference to the underlying `datetime` value or throws an exception.
     * \par Exception safety
     * Strong guarantee. Throws on type mismatch.
     * \throws bad_field_access If `!this->is_datetime()`
     *
     * \par Object lifetimes
     * The returned reference is valid as long as `*this` is alive and no function that invalidates
     * references is called on `*this`.
     */
    const datetime& as_datetime() const { return repr_.as<datetime>(); }

    /**
     * \brief Retrieves a reference to the underlying `time` value or throws an exception.
     * \par Exception safety
     * Strong guarantee. Throws on type mismatch.
     * \throws bad_field_access If `!this->is_time()`
     *
     * \par Object lifetimes
     * The returned reference is valid as long as `*this` is alive and no function that invalidates
     * references is called on `*this`.
     */
    const time& as_time() const { return repr_.as<time>(); }

    /// \copydoc as_int64
    std::int64_t& as_int64() { return repr_.as<std::int64_t>(); }

    /// \copydoc as_uint64
    std::uint64_t& as_uint64() { return repr_.as<std::uint64_t>(); }

    /// \copydoc as_string
    std::string& as_string() { return repr_.as<std::string>(); }

    /// \copydoc as_blob
    blob& as_blob() { return repr_.as<blob>(); }

    /// \copydoc as_float
    float& as_float() { return repr_.as<float>(); }

    /// \copydoc as_double
    double& as_double() { return repr_.as<double>(); }

    /// \copydoc as_date
    date& as_date() { return repr_.as<date>(); }

    /// \copydoc as_datetime
    datetime& as_datetime() { return repr_.as<datetime>(); }

    /// \copydoc as_time
    time& as_time() { return repr_.as<time>(); }

    /**
     * \brief Retrieves a reference to the underlying `std::int64_t` value (unchecked access).
     * \par Preconditions
     * `this->is_int64() == true` (if violated, results in undefined behavior).
     *
     * \par Exception safety
     * No-throw guarantee.
     *
     * \par Object lifetimes
     * The returned reference is valid as long as `*this` is alive and no function that invalidates
     * references is called on `*this`.
     */
    const std::int64_t& get_int64() const noexcept { return repr_.get<std::int64_t>(); }

    /**
     * \brief Retrieves a reference to the underlying `std::uint64_t` value (unchecked access).
     * \par Preconditions
     * `this->is_uint64() == true` (if violated, results in undefined behavior).
     *
     * \par Exception safety
     * No-throw guarantee.
     *
     * \par Object lifetimes
     * The returned reference is valid as long as `*this` is alive and no function that invalidates
     * references is called on `*this`.
     */
    const std::uint64_t& get_uint64() const noexcept { return repr_.get<std::uint64_t>(); }

    /**
     * \brief Retrieves a reference to the underlying `std::string` value (unchecked access).
     * \par Preconditions
     * `this->is_string() == true` (if violated, results in undefined behavior).
     *
     * \par Exception safety
     * No-throw guarantee.
     *
     * \par Object lifetimes
     * The returned reference is valid as long as `*this` is alive and no function that invalidates
     * references is called on `*this`.
     */
    const std::string& get_string() const noexcept { return repr_.get<std::string>(); }

    /**
     * \brief Retrieves a reference to the underlying `blob` value (unchecked access).
     * \par Preconditions
     * `this->is_blob() == true` (if violated, results in undefined behavior).
     *
     * \par Exception safety
     * No-throw guarantee.
     *
     * \par Object lifetimes
     * The returned reference is valid as long as `*this` is alive and no function that invalidates
     * references is called on `*this`.
     */
    const blob& get_blob() const noexcept { return repr_.get<blob>(); }

    /**
     * \brief Retrieves a reference to the underlying `float` value (unchecked access).
     * \par Preconditions
     * `this->is_float() == true` (if violated, results in undefined behavior).
     *
     * \par Exception safety
     * No-throw guarantee.
     *
     * \par Object lifetimes
     * The returned reference is valid as long as `*this` is alive and no function that invalidates
     * references is called on `*this`.
     */
    const float& get_float() const noexcept { return repr_.get<float>(); }

    /**
     * \brief Retrieves a reference to the underlying `double` value (unchecked access).
     * \par Preconditions
     * `this->is_double() == true` (if violated, results in undefined behavior).
     *
     * \par Exception safety
     * No-throw guarantee.
     *
     * \par Object lifetimes
     * The returned reference is valid as long as `*this` is alive and no function that invalidates
     * references is called on `*this`.
     */
    const double& get_double() const noexcept { return repr_.get<double>(); }

    /**
     * \brief Retrieves a reference to the underlying `date` value (unchecked access).
     * \par Preconditions
     * `this->is_date() == true` (if violated, results in undefined behavior).
     *
     * \par Exception safety
     * No-throw guarantee.
     *
     * \par Object lifetimes
     * The returned reference is valid as long as `*this` is alive and no function that invalidates
     * references is called on `*this`.
     */
    const date& get_date() const noexcept { return repr_.get<date>(); }

    /**
     * \brief Retrieves a reference to the underlying `datetime` value (unchecked access).
     * \par Preconditions
     * `this->is_datetime() == true` (if violated, results in undefined behavior).
     *
     * \par Exception safety
     * No-throw guarantee.
     *
     * \par Object lifetimes
     * The returned reference is valid as long as `*this` is alive and no function that invalidates
     * references is called on `*this`.
     */
    const datetime& get_datetime() const noexcept { return repr_.get<datetime>(); }

    /**
     * \brief Retrieves a reference to the underlying `time` value (unchecked access).
     * \par Preconditions
     * `this->is_time() == true` (if violated, results in undefined behavior).
     *
     * \par Exception safety
     * No-throw guarantee.
     *
     * \par Object lifetimes
     * The returned reference is valid as long as `*this` is alive and no function that invalidates
     * references is called on `*this`.
     */
    const time& get_time() const noexcept { return repr_.get<time>(); }

    /// \copydoc get_int64
    std::int64_t& get_int64() noexcept { return repr_.get<std::int64_t>(); }

    /// \copydoc get_uint64
    std::uint64_t& get_uint64() noexcept { return repr_.get<std::uint64_t>(); }

    /// \copydoc get_string
    std::string& get_string() noexcept { return repr_.get<std::string>(); }

    /// \copydoc get_blob
    blob& get_blob() noexcept { return repr_.get<blob>(); }

    /// \copydoc get_float
    float& get_float() noexcept { return repr_.get<float>(); }

    /// \copydoc get_double
    double& get_double() noexcept { return repr_.get<double>(); }

    /// \copydoc get_date
    date& get_date() noexcept { return repr_.get<date>(); }

    /// \copydoc get_datetime
    datetime& get_datetime() noexcept { return repr_.get<datetime>(); }

    /// \copydoc get_time
    time& get_time() noexcept { return repr_.get<time>(); }

    /**
     * \brief Constructs a \ref field_view pointing to `*this`.
     * \details The resulting `field_view` has the same kind and value as `*this`.
     *
     * \par Exception safety
     * No-throw guarantee.
     *
     * \par Object lifetimes
     * The returned object acts as a
     * reference to `*this`, and will be valid as long as `*this` is alive.
     */
    inline operator field_view() const noexcept { return field_view(&repr_); }

private:
    detail::field_impl repr_;

    inline void from_view(const field_view& v);
};

/**
 * \relates field
 * \brief Tests for equality.
 * \details The same considerations as \ref field_view::operator== apply.
 *
 * \par Exception safety
 * No-throw guarantee.
 */
inline bool operator==(const field& lhs, const field& rhs) noexcept
{
    return field_view(lhs) == field_view(rhs);
}

/**
 * \relates field
 * \brief Tests for inequality.
 * \par Exception safety
 * No-throw guarantee.
 */
inline bool operator!=(const field& lhs, const field& rhs) noexcept { return !(lhs == rhs); }

/**
 * \relates field
 * \brief Tests for equality.
 * \details The same considerations as \ref field_view::operator== apply.
 *
 * \par Exception safety
 * No-throw guarantee.
 */
inline bool operator==(const field_view& lhs, const field& rhs) noexcept { return lhs == field_view(rhs); }

/**
 * \relates field
 * \brief Tests for inequality.
 * \par Exception safety
 * No-throw guarantee.
 */
inline bool operator!=(const field_view& lhs, const field& rhs) noexcept { return !(lhs == rhs); }

/**
 * \relates field
 * \brief Tests for equality.
 * \details The same considerations as \ref field_view::operator== apply.
 * \par Exception safety
 * No-throw guarantee.
 */
inline bool operator==(const field& lhs, const field_view& rhs) noexcept { return field_view(lhs) == rhs; }

/**
 * \relates field
 * \brief Tests for inequality.
 * \par Exception safety
 * No-throw guarantee.
 */
inline bool operator!=(const field& lhs, const field_view& rhs) noexcept { return !(lhs == rhs); }

/**
 * \relates field
 * \brief Streams a `field`.
 */
inline std::ostream& operator<<(std::ostream& os, const field& v);

}  // namespace mysql
}  // namespace boost

#include <boost/mysql/impl/field.hpp>

#endif
