// bdljsn_error.h                                                     -*-C++-*-
#ifndef INCLUDED_BDLJSN_ERROR
#define INCLUDED_BDLJSN_ERROR

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@PURPOSE: Provide a description of an error processing a document.
//
//@CLASSES:
//  bdljsn::Error: a description of a document processing error
//
//@SEE_ALSO: bdljsn_jsonutil, bdljsn_json
//
//@DESCRIPTION: This component provides a single, un-constrained
// (value-semantic) attribute class, 'bdljsn::Error', that is used to describe
// an error in the occured processing a (JSON) document.
//
///Attributes
///----------
//..
//  Name                Type          Default
//  ------------------  -----------   -------
//  location            Location      Location(0)
//  message             string        ""
//..
//: o 'location': the location in the document where the error occured
//: o 'message': a description of the error that occured
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Populating an 'bdljsn::Error' Object
///- - - - - - - - - - - - - - - - - - - - - - - -
// This component is designed to describe an error that occured when processing
// a (JSON) document.  Suppose we are implementing a function,
// 'extractIntegerToken', that parses a numeric token and obtains an 'int'
// value:
//
// First, we define the function signature:
//..
//  int extractIntegerToken(int              *value,
//                          bdljsn::Error    *error,
//                          bsl::string_view  inputText)
//      // Load to the specified 'value' the 'int' value represented by the
//      // specified 'inputText'.  Return 0 on success, and a non-zero value
//      // otherwise with no effect on '*value' and the specified 'error' is
//      // set.
//  {
//      BSLS_ASSERT(value);
//      BSLS_ASSERT(error);
//
//      enum { e_SUCCESS, e_FAILURE };
//      // ...
//..
// Then, we attempt to exact a 'int' value from the 'inputText':
//..
//      int                                      result;
//      bsl::pair<MyParseStatus::Enum, unsigned> status =
//                                 MyNumericUtil::parseInt(&result, inputText);
//..
// Now, we check the parse status and if unsuccessful, we use the status
// information to set the 'bsljsn::Error' object expected by our caller:
//..
//      if (MyParseStatus::e_OK != status.first) {
//          unsigned position = status.second;
//          error->setLocation(bdljsn::Location(static_cast<bsl::uint64_t>(
//                                                                 position)));
//          error->setMessage(MyParseStatus::toAscii(status.first));
//          return e_FAILURE;                                         // RETURN
//      }
//..
// Finally, if the parse was successful, set the output parameter and return
// with status value that indicates success.
//..
//      *value = result;
//      return e_SUCCESS;
//  }
//..

#include <bdlscm_version.h>

#include <bdljsn_location.h>

#include <bslh_hash.h>     // 'bslh::hashAppend'

#include <bslalg_swaputil.h>

#include <bslmf_isbitwisemoveable.h>
#include <bslmf_nestedtraitdeclaration.h>

#include <bsls_assert.h>
#include <bsls_keyword.h>

#include <bsl_ostream.h>
#include <bsl_string.h>
#include <bsl_string_view.h>

namespace BloombergLP {
namespace bdljsn {

                                // ===========
                                // class Error
                                // ===========

class Error {
    // This unconstrained (value-semantic) attribute class specifies a
    // description of an error in processing a (JSON) document.  See the
    // {Attributes} section under {DESCRIPTION} in the component-level
    // documentation for information on the class attributes.  Note that the
    // class invariants are identically the constraints on the individual
    // attributes.

    // DATA
    Location    d_location;  // location where the error occurred
    bsl::string d_message;   // a description of the error

    // FRIENDS
    friend void swap(Error& , Error& );

  public:
    // TRAITS
    BSLMF_NESTED_TRAIT_DECLARATION(Error, bslma::UsesBslmaAllocator);
    BSLMF_NESTED_TRAIT_DECLARATION(Error, bslmf::IsBitwiseMoveable);

    // CREATORS
    Error();
    explicit Error(bslma::Allocator *basicAllocator);
        // Create an 'Error' object having the default value (see
        // {Attributes}).  Optionally specify a 'basicAllocator' used to supply
        // memory.  If 'basicAllocator' is 0, the currently installed default
        // allocator is used.

    Error(const Location&          location,
          const bsl::string_view&  message,
          bslma::Allocator        *basicAllocator = 0);
        // Create an 'Error' object having the specified 'location' and
        // 'message'.  Optionally specify a 'basicAllocator' used to supply
        // memory.  If 'basicAllocator' is 0, the currently installed default
        // allocator is used.

    Error(const Error& original, bslma::Allocator *basicAllocator = 0);
        // Create an 'Error' object having the value of the specified
        // 'original' object.  Optionally specify a 'basicAllocator' used to
        // supply memory.  If 'basicAllocator' is 0, the currently installed
        // default allocator is used.

    Error(bslmf::MovableRef<Error> original) BSLS_KEYWORD_NOEXCEPT;
        // Create a 'Error' object having the same value and the same allocator
        // as the specified 'original' object.  The value of 'original' becomes
        // unspecified but valid, and its allocator remains unchanged.

    Error(bslmf::MovableRef<Error>  original,
          bslma::Allocator         *basicAllocator);
        // Create a 'Error' object having the same value as the specified
        // 'original' object, and using the specified 'basicAllocator' to
        // supply memory.  If 'basicAllocator' is 0, the currently installed
        // default allocator is used.  The allocator of 'original' remains
        // unchanged.  If 'original' and the newly created object have the same
        // allocator then the value of 'original' becomes unspecified but
        // valid, and no exceptions will be thrown; otherwise 'original' is
        // unchanged and an exception may be thrown.

    ~Error();
        // Destroy this object.

    // MANIPULATORS
    Error& operator=(const Error& rhs);
        // Assign to this object the value of the specified 'rhs' object, and
        // return a non-'const' reference to this object.

    Error& operator=(bslmf::MovableRef<Error> rhs);
        // Assign to this object the value of the specified 'rhs' object, and
        // return a non-'const' reference to this object.  The allocators of
        // this object and 'rhs' both remain unchanged.  If 'rhs' and this
        // object have the same allocator then the value of 'rhs' becomes
        // unspecified but valid, and no exceptions will be thrown; otherwise
        // 'rhs' is unchanged (and an exception may be thrown).

    Error& reset();
        // Reset this object to the default value (i.e., its value upon default
        // construction).

    Error& setLocation(const Location& value);
        // Set the 'location' attribute of this object to the specified
        // 'value'.

    Error& setMessage(const bsl::string_view& value);
        // Set the 'message' attribute of this object to the specified 'value'.

                        // Aspects

    void swap(Error& other);
        // Efficiently exchange the value of this object with the value of the
        // specified 'other' object.  This method provides the no-throw
        // exception-safety guarantee.  The behavior is undefined unless this
        // object was created with the same allocator as 'other'.

    // ACCESSORS
    const Location& location() const;
        // Return the 'location' attribute of this object.

    const bsl::string&  message() const;
        // Return the 'message' attribute of this object.

                                  // Aspects

    bslma::Allocator *allocator() const;
        // Return the allocator used by this object to supply memory.  Note
        // that if no allocator was supplied at construction the default
        // allocator in effect at construction is used.

    bsl::ostream& print(bsl::ostream& stream,
                        int           level = 0,
                        int           spacesPerLevel = 4) const;
        // Write the value of this object to the specified output 'stream' in a
        // human-readable format, and return a non-'const' reference to
        // 'stream'.  Optionally specify an initial indentation 'level', whose
        // absolute value is incremented recursively for nested objects.  If
        // 'level' is specified, optionally specify 'spacesPerLevel', whose
        // absolute value indicates the number of spaces per indentation level
        // for this and all of its nested objects.  If 'level' is negative,
        // suppress indentation of the first line.  If 'spacesPerLevel' is
        // negative, format the entire output on one line, suppressing all but
        // the initial indentation (as governed by 'level').  If 'stream' is
        // not valid on entry, this operation has no effect.  Note that the
        // format is not fully specified, and can change without notice.
};

// FREE OPERATORS
bool operator==(const Error& lhs, const Error& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' attribute objects have
    // the same value, and 'false' otherwise.  Two attribute objects have the
    // same value if each respective attribute has the same value.

bool operator!=(const Error& lhs, const Error& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' attribute objects do not
    // have the same value, and 'false' otherwise.  Two attribute objects do
    // not have the same value if one or more respective attributes differ in
    // values.

bsl::ostream& operator<<(bsl::ostream& stream, const Error& object);
    // Write the value of the specified 'object' to the specified output
    // 'stream' in a single-line format, and return a non-'const' reference to
    // 'stream'.  If 'stream' is not valid on entry, this operation has no
    // effect.  Note that this human-readable format is not fully specified and
    // can change without notice.  Also note that this method has the same
    // behavior as 'object.print(stream, 0, -1)', but with the attribute names
    // elided.

// FREE FUNCTIONS
template <class HASHALG>
void hashAppend(HASHALG& hashAlgorithm, const Error& object);
    // Pass the specified 'object' to the specified 'hashAlgorithm'.  This
    // function integrates with the 'bslh' modular hashing system and
    // effectively provides a 'bsl::hash' specialization for 'ErroError'.

void swap(Error& a, Error& b);
    // Exchange the values of the specified 'a' and 'b' objects.  This function
    // provides the no-throw exception-safety guarantee if the two objects were
    // created with the same allocator and the basic guarantee otherwise.

// ============================================================================
//                         INLINE DEFINITIONS
// ============================================================================

                                // -----------
                                // class Error
                                // -----------

// CREATORS
inline
Error::Error()
: d_location()
, d_message()
{
}

inline
Error::Error(bslma::Allocator *basicAllocator)
: d_location()
, d_message(basicAllocator)
{
}

inline
Error::Error(const Location&          location,
             const bsl::string_view&  message,
             bslma::Allocator        *basicAllocator)
: d_location(location)
, d_message(message, basicAllocator)
{
}

inline
Error::Error(const Error& original, bslma::Allocator *basicAllocator)
: d_location(original.d_location)
, d_message(original.d_message, basicAllocator)
{
}

inline
Error::Error(bslmf::MovableRef<Error> original) BSLS_KEYWORD_NOEXCEPT
: d_location(bslmf::MovableRefUtil::move(
                           bslmf::MovableRefUtil::access(original).d_location))
, d_message(bslmf::MovableRefUtil::move(
                            bslmf::MovableRefUtil::access(original).d_message))
{
}

inline
Error::Error(bslmf::MovableRef<Error>  original,
             bslma::Allocator         *basicAllocator)
: d_location(bslmf::MovableRefUtil::move(
                           bslmf::MovableRefUtil::access(original).d_location))
, d_message(bslmf::MovableRefUtil::move(
                            bslmf::MovableRefUtil::access(original).d_message),
            basicAllocator)
{
}

inline
Error::~Error()
{
}

// MANIPULATORS
inline
Error& Error::operator=(const Error& rhs)
{
    if (this != &rhs) {
        d_message  = rhs.d_message;  // do first for strong guarantee
        d_location = rhs.d_location;
    }

    return *this;
}

inline
Error& Error::operator=(bslmf::MovableRef<Error> rhs)
{
    Error error(bslmf::MovableRefUtil::move(rhs), allocator());
    error.swap(*this);
    return *this;
}

inline
Error& Error::reset()
{
    d_location.reset();
    d_message.clear();
    return *this;
}

inline
Error& Error::setLocation(const Location& value)
{
    d_location = value;
    return *this;
}

inline
Error& Error::setMessage(const bsl::string_view& value)
{
    d_message = value;
    return *this;
}

                        // Aspects

inline
void Error::swap(Error& other)
{
    BSLS_ASSERT(allocator() == other.allocator());

    bslalg::SwapUtil::swap(&d_location, &other.d_location);
    bslalg::SwapUtil::swap(&d_message,  &other.d_message);
}

// ACCESSORS
inline
const Location& Error::location() const
{
    return d_location;
}

inline
const bsl::string& Error::message() const
{
    return d_message;
}

                        // Aspects

inline
bslma::Allocator *Error::allocator() const
{
    return d_message.get_allocator().mechanism();
}

}  // close package namespace

// FREE OPERATORS
inline
bool bdljsn::operator==(const bdljsn::Error& lhs, const bdljsn::Error& rhs)
{
    return lhs.location() == rhs.location() && lhs.message() == rhs.message();
}

inline
bool bdljsn::operator!=(const bdljsn::Error& lhs, const bdljsn::Error& rhs)
{
    return lhs.location() != rhs.location() || lhs.message() != rhs.message();
}

// FREE FUNCTIONS
template <class HASHALG>
inline
void bdljsn::hashAppend(HASHALG& hashAlgorithm, const Error& object)
{
    using ::BloombergLP::bslh::hashAppend;
    hashAppend(hashAlgorithm, object.location());
    hashAppend(hashAlgorithm, object.message());
}

inline
void bdljsn::swap(Error& a, Error& b)
{
    bslalg::SwapUtil::swap(&a.d_location, &b.d_location);
    bslalg::SwapUtil::swap(&a.d_message,  &b.d_message);
}

}  // close enterprise namespace

#endif

// ----------------------------------------------------------------------------
// Copyright 2022 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------