// bdljsn_readoptions.h                                               -*-C++-*-
#ifndef INCLUDED_BDLJSN_READOPTIONS
#define INCLUDED_BDLJSN_READOPTIONS

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

//@PURPOSE: Provide options for reading a JSON document.
//
//@CLASSES:
//  bdljsn::ReadOptions: options for reading a JSON document
//
//@SEE_ALSO: bdljsn_jsonutil, bdljsn_json
//
//@DESCRIPTION: This component provides a single, simply constrained
// (value-semantic) attribute class, `bdljsn::ReadOptions`, that is used to
// specify options for reading a JSON document (see `bdljsn_jsonutil`).
//
///Attributes
///----------
// ```
// Name                Type           Default         Simple Constraints
// ------------------  -----------    -------         ------------------
// maxNestedDepth      int            64              > 0
// allowTrailingText   bool           false
// ```
// * `maxNestedDepth`: the maximum depth to which JSON objects and arrays are
//   allowed to be nested before the JSON decoder reports an error.  For
//   example, if `maxNestedDepth` is 8, and a JSON text has 9 consecutive open
//   brackets (`[`) then decoding will return an error.  This option can be
//   used to prevent poorly formed (or malicious) JSON text from causing a
//   stack overflow.
// * `allowTrailingText`: whether a read operation will report an error
//   if any non-white space text follows a valid JSON document.  By default
//   this option is `false`, indicating the user expects the input to contain
//   a single valid JSON document (without any subsequent text).  When
//   set to `true` a `read` operation will return success if there is text
//   following a valid JSON document, assuming that text is separated by
//   a delimeter.  See {`bdljsn_jsonutil`} for details.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Creating and Populating an Options Object
/// - - - - - - - - - - - - - - - - - - - - - - - - - -
// This component is designed to be used at a higher level to set the options
// for decoding `Datum` objects in the JSON format.  This example shows how to
// create and populate an options object.
//
// First, we default-construct a `bdljsn::ReadOptions` object:
// ```
// const int MAX_NESTED_DEPTH = 16;
//
// bdljsn::ReadOptions options;
// assert(64    == options.maxNestedDepth());
// assert(false == options.allowTrailingText());
// ```
// Finally, we populate that object to limit the maximum nested depth using a
// pre-defined limit:
// ```
// options.setMaxNestedDepth(MAX_NESTED_DEPTH);
// assert(MAX_NESTED_DEPTH == options.maxNestedDepth());
// ```

#include <bdlscm_version.h>

#include <bsl_iosfwd.h>

#include <bsls_assert.h>

namespace BloombergLP {
namespace bdljsn {

                             // =================
                             // class ReadOptions
                             // =================

/// This simply constrained (value-semantic) attribute class specifies options
/// for reading a JSON document.  See the [](#Attributes) for information on
/// the class attributes.  Note that the class invariants are identically the
/// constraints on the individual attributes.
class ReadOptions {

    // INSTANCE DATA

    // whether to permit text after a valid JSON document
    bool d_allowTrailingText;

    // maximum nesting level for JSON objects and arrays
    int d_maxNestedDepth;

  public:
    // CONSTANTS
    static const bool s_DEFAULT_INITIALIZER_ALLOW_TRAILING_TEXT;
    static const int  s_DEFAULT_INITIALIZER_MAX_NESTED_DEPTH;

  public:
    // CREATORS

    /// Create an object of type `ReadOptions` having the (default) attribute
    /// values:
    /// ```
    /// setAllowTrailingText() == false
    /// maxNestedDepth()       == 64
    /// ```
    ReadOptions();

    /// Create an object of type `ReadOptions` having the value of the
    /// specified `original` object.
    ReadOptions(const ReadOptions& original);

    /// Destroy this object.
    ~ReadOptions();

    // MANIPULATORS

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

    /// Reset this object to the default value (i.e., its value upon default
    /// construction) and return a non-`const` reference to this object.
    ReadOptions& reset();

    /// Set the `allowTrailingText` attribute of this object to th specified
    /// `value` and return a non-`const` reference to this object.
    ReadOptions& setAllowTrailingText(bool value);

    /// Set the `maxNestedDepth` attribute of this object to the specified
    /// `value` and return a non-`const` reference to this object.  The
    /// behavior is undefined unless `0 < value`.
    ReadOptions& setMaxNestedDepth(int value);

    // ACCESSORS

    /// Return the `allowTrailingText` attribute of this object.
    bool allowTrailingText() const;

    /// Return the `maxNestedDepth` attribute of this object.
    int maxNestedDepth() const;

                                  // Aspects

    /// Format this object to the specified output `stream` at the optionally
    /// specified indentation `level` and return a reference to the modifiable
    /// `stream`.  If `level` is specified, optionally specify
    /// `spacesPerLevel`, the number of spaces per indentation level for this
    /// and all of its nested objects.  Each line is indented by the absolute
    /// value of `level * spacesPerLevel`.  If `level` is negative, suppress
    /// indentation of the first line.  If `spacesPerLevel` is negative,
    /// suppress line breaks and format the entire output on one line.  If
    /// `stream` is initially invalid, this operation has no effect.  Note that
    /// a trailing newline is provided in multiline mode only.  Also note that
    /// the format is not fully specified, and can change without notice.
    bsl::ostream& print(bsl::ostream& stream,
                        int           level          = 0,
                        int           spacesPerLevel = 4) const;
};

// FREE OPERATORS

/// 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.
inline
bool operator==(const ReadOptions& lhs, const ReadOptions& 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 do not have the
/// same value.
inline
bool operator!=(const ReadOptions& lhs, const ReadOptions& rhs);

/// Format the specified `rhs` to the specified output `stream` in a single
/// line format and return a non-`const` reference to `stream`.
inline
bsl::ostream& operator<<(bsl::ostream& stream, const ReadOptions& rhs);

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

                             // -----------------
                             // class ReadOptions
                             // -----------------
// CREATORS
inline
ReadOptions::ReadOptions(const ReadOptions& original)
: d_allowTrailingText(original.d_allowTrailingText)
, d_maxNestedDepth   (original.d_maxNestedDepth)
{
}

inline
ReadOptions::~ReadOptions()
{
    BSLS_ASSERT(0 < d_maxNestedDepth);
}

// MANIPULATORS
inline
ReadOptions& ReadOptions::operator=(const ReadOptions& rhs)
{
    d_allowTrailingText = rhs.d_allowTrailingText;
    d_maxNestedDepth    = rhs.d_maxNestedDepth;

    return *this;
}

inline
ReadOptions& ReadOptions::setAllowTrailingText(bool value)
{
    d_allowTrailingText = value;
    return *this;
}

inline
ReadOptions& ReadOptions::setMaxNestedDepth(int value)
{
    BSLS_ASSERT(0 < value);

    d_maxNestedDepth = value;
    return *this;
}

// ACCESSORS
inline
bool ReadOptions::allowTrailingText() const
{
    return d_allowTrailingText;
}

inline
int ReadOptions::maxNestedDepth() const
{
    return d_maxNestedDepth;
}

}  // close package namespace

// FREE OPERATORS
inline
bool bdljsn::operator==(const bdljsn::ReadOptions& lhs,
                        const bdljsn::ReadOptions& rhs)
{
    return lhs.maxNestedDepth()    == rhs.maxNestedDepth()
        && lhs.allowTrailingText() == rhs.allowTrailingText();
}

inline
bool bdljsn::operator!=(const bdljsn::ReadOptions& lhs,
                        const bdljsn::ReadOptions& rhs)
{
    return lhs.maxNestedDepth()    != rhs.maxNestedDepth()
        || lhs.allowTrailingText() != rhs.allowTrailingText();
}

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

}  // 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 ----------------------------------
