// bdld_datumerror.h                                                  -*-C++-*-

#ifndef INCLUDED_BDLD_DATUMERROR
#define INCLUDED_BDLD_DATUMERROR

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

//@PURPOSE: Provide a type for an error code with an optional error message.
//
//@CLASSES:
//  bdld::DatumError: type for an error code with an optional error message
//
//@SEE_ALSO: bdld_datum, bdld_datumudt
//
//@DESCRIPTION: This component defines a *complex-constrained* *value-semantic*
// *attribute* class 'bdld::DatumError' representing an error code with an
// optional descriptive error message.  This component holds a reference to the
// error message that was supplied at construction.  Accessors inside 'Datum'
// class that need to return an error value, return an instance of
// 'DatumError'.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Basic 'DatumError' usage
///- - - - - - - - - - - - - - - - - -
// Suppose we need a function to verify if newly created password meets basic
// security requirements.  Password must contain at least one uppercase letter,
// one lowercase letter, one numeral and one special symbol.  The following
// code illustrates how to use 'bdld::DatumError' to notify user about
// password weaknesses.
//
// First, we need to write a verification function:
//..
//  bdld::DatumError verifyNewPassword(const char *password)
//      // Verify if specified 'password' meets basic security requirements.
//  {
//      bool uppercasePresence     = false;
//      bool lowercasePresence     = false;
//      bool numeralPresence       = false;
//      bool specialSymbolPresence = false;
//..
// Passed string analysis:
//..
//      while (*password) {
//          if (*password >= 'A' && *password <= 'Z') {
//              uppercasePresence = true;
//          }
//          if (*password >= 'a' && *password <= 'z') {
//              lowercasePresence = true;
//          }
//          if (*password >= '0' && *password <= '9') {
//              numeralPresence = true;
//          }
//          if (*password >= '!' && *password <= '.') {
//              specialSymbolPresence = true;
//          }
//          ++password;
//      }
//..
// Result compilation:
//..
//      bdld::DatumError result;
//
//      if (!uppercasePresence) {
//          result = bdld::DatumError(1, bslstl::StringRef("Uppercase"));
//      } else if (!lowercasePresence) {
//          result = bdld::DatumError(2, bslstl::StringRef("Lowercase"));
//      } else if (!numeralPresence) {
//          result = bdld::DatumError(3, bslstl::StringRef("Numeral"));
//      } else if (!specialSymbolPresence) {
//          result = bdld::DatumError(4, bslstl::StringRef("Special"));
//      }
//
//      return result;
//  }
//..
// Next, we need to create password for verification and call our function:
//..
//  bdld::DatumError error = verifyNewPassword("Test");
//..
// Then, check the results:
//..
//  assert(bdld::DatumError() != error);
//  assert(3                  == error.code());
//..
// Finally, we can print the result to the output stream:
//..
//  ostringstream out;
//  error.print(out);
//..

#include <bdlscm_version.h>

#include <bdlb_printmethods.h>

#include <bslmf_istriviallycopyable.h>
#include <bslmf_nestedtraitdeclaration.h>

#include <bsls_assert.h>

#include <bsl_string.h>
#include <bsl_iosfwd.h>

namespace BloombergLP {

namespace bdld {
                               // ================
                               // class DatumError
                               // ================
class DatumError {
    // This component 'class' provides a *complex constrained* attribute type,
    // 'bdld::DatumError', that represents an error code with an optional
    // descriptive error message.  Note that this class holds a reference to
    // the error message and does not make a copy of it.

  private:
    // DATA
    int               d_code;     // error code
    bslstl::StringRef d_message;  // error message

  public:
    // TRAITS
    BSLMF_NESTED_TRAIT_DECLARATION(DatumError, bsl::is_trivially_copyable);
    BSLMF_NESTED_TRAIT_DECLARATION(DatumError, bdlb::HasPrintMethod);

    // CREATORS
    DatumError();
        // Create a 'DatumError' object having the default error code of 0 and
        // an empty error message.

    explicit DatumError(int code);
        // Create a 'DatumError' object having the specified error 'code' value
        // and and empty error message.

    DatumError(int code, const bslstl::StringRef& message);
        // Create a 'DatumError' object having the specified error 'code' value
        // and the specified error 'message' value.  Note that the 'message' is
        // held by the reference and not copied.

    //! DatumError(const DatumError& origin) = default;
    //! ~DatumError() = default;

    // MANIPULATORS
    //! DatumError& operator=(const DatumError& rhs) = default;

    // ACCESSORS
    int code() const;
        // Return the error code.

    bslstl::StringRef message() const;
        // Return a reference to the non-modifyable error message.  The
        // returned reference remains valid as long as the underlying message
        // is not modified or destroyed -- irrespective of the state (or
        // existence) of this object.  The behavior is undefined unless the
        // underlying error message has been modified or destroyed since this
        // object was created.

    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 reference to the modifyable
        // '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, suppress all but the initial indentation (as governed by
        // 'level').  For readability the entire output is formatted on one
        // line, regardless of the 'spacesPerLevel' value. 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.
};

// FREE OPERATORS
bool operator==(const DatumError& lhs, const DatumError& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' have the same value and
    // 'false' otherwise.  Two 'DatumError' objects have the same value if they
    // have same error code and message values.

bool operator!=(const DatumError& lhs, const DatumError& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' have different values and
    // 'false' otherwise.  Two 'DatumError' objects have different values if
    // they have different error code or message values.

bool operator<(const DatumError& lhs, const DatumError& rhs);
    // Return 'true' if value of the specified 'lhs' is less than value of the
    // specified 'rhs' and 'false' otherwise.  Value of 'lhs' is less than
    // value of 'rhs' if error code value of 'lhs' is less than error code
    // value of 'rhs', or they have the same error code value and error message
    // value of 'lhs' is less than error message value of 'rhs'.

bool operator<=(const DatumError& lhs, const DatumError& rhs);
    // Return 'true' if value of the specified 'lhs' is less than or equal to
    // value of the specified 'rhs' and 'false' otherwise.  Value of 'lhs' is
    // less than or equal to value of 'rhs' if error code value of 'lhs' is
    // less than or equal to error code value of 'rhs', or they have the same
    // error code value and error message value of 'lhs' is less than or equal
    // to error message value of 'rhs'.

bool operator>(const DatumError& lhs, const DatumError& rhs);
    // Return 'true' if value of the specified 'lhs' is greater than value of
    // the specified 'rhs' and 'false' otherwise.  Value of 'lhs' is greater
    // than value of 'rhs' if error code value of 'lhs' is greater than error
    // code value of 'rhs', or they have the same error code value and error
    // message value of 'lhs' is greater than error message value of 'rhs'.

bool operator>=(const DatumError& lhs, const DatumError& rhs);
    // Return 'true' if value of the specified 'lhs' is greater than or equal
    // to value of the specified 'rhs' and 'false' otherwise.  Value of 'lhs'
    // is greater than or equal to value of 'rhs' if error code value of 'lhs'
    // is greater than or equal to error code value of 'rhs', or they have the
    // same error code value and error message value of 'lhs' is greater than
    // or equal to error message value of 'rhs'.

bsl::ostream& operator<<(bsl::ostream& stream, const DatumError& rhs);
    // Write the value of the specified 'rhs' object to the specified output
    // 'stream' in a single-line format, and return a reference to the
    // modifyable 'stream'.  If 'stream' is not valid on entry, this operation
    // has no effect.  Note that this human-readable format is not fully
    // specified, can change without notice, and is logically equivalent to:
    //..
    //  print(stream, 0, -1);
    //..
    // Single line output format for the 'DatumError' object is shown below:
    //..
    //  error(n)
    //  error(n,'msg')
    //..
    // (where n is the integer error code value and 'msg' is the error message
    // value in single quotes).  Note that the first version will be output if
    // there is no error message string.

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

                                // ----------------
                                // class DatumError
                                // ----------------
// CREATORS
inline
DatumError::DatumError()
: d_code(0)
{
}

inline
DatumError::DatumError(int code)
: d_code(code)
{
}

inline
DatumError::DatumError(int code, const bslstl::StringRef& message)
: d_code(code)
, d_message(message)
{
}

// ACCESSORS
inline
int DatumError::code() const
{
    return d_code;
}

inline
bslstl::StringRef DatumError::message() const
{
    return d_message;
}

}  // close package namespace

// FREE OPERATORS
inline
bool bdld::operator==(const DatumError& lhs, const DatumError& rhs)
{
    return (lhs.code() == rhs.code() && lhs.message() == rhs.message());
}

inline
bool bdld::operator!=(const DatumError& lhs, const DatumError& rhs)
{
    return !(lhs == rhs);
}

inline
bool bdld::operator<(const DatumError& lhs, const DatumError& rhs)
{
    return (lhs.code() < rhs.code() ||
            (lhs.code() == rhs.code() && lhs.message() < rhs.message()));
}

inline
bool bdld::operator<=(const DatumError& lhs, const DatumError& rhs)
{
    return (lhs == rhs || lhs < rhs);
}

inline
bool bdld::operator>(const DatumError& lhs, const DatumError& rhs)
{
    return !(lhs <= rhs);
}

inline
bool bdld::operator>=(const DatumError& lhs, const DatumError& rhs)
{
    return !(lhs < rhs);
}

inline
bsl::ostream& bdld::operator<<(bsl::ostream& stream, const DatumError& rhs)
{
    return rhs.print(stream, 0, -1);
}

}  // close enterprise namespace

#endif

// ----------------------------------------------------------------------------
// Copyright 2015 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 ----------------------------------