# generate-exceptions: generate C++ files for Xapian's exception hierarchy.
#
# Copyright (C) 2003-2024 Olly Betts
# Copyright (C) 2007 Richard Boulton
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see
# <https://www.gnu.org/licenses/>.

use strict;
use exception_data qw(
    $copyright $generated_warning @baseclasses @classes %classcode
);
use File::Path qw(mkpath);

mkpath("include/xapian");
open HDR, ">include/xapian/error.h" or die $!;
open DISPATCH, ">include/xapian/errordispatch.h" or die $!;

print HDR <<'EOF';
/** @file
 *  @brief Hierarchy of classes which Xapian can throw as exceptions.
 */
EOF

print HDR $generated_warning;
print DISPATCH $generated_warning;

print HDR $copyright;
print DISPATCH $copyright;

print HDR <<'EOF';

#ifndef XAPIAN_INCLUDED_ERROR_H
#define XAPIAN_INCLUDED_ERROR_H

#if !defined XAPIAN_IN_XAPIAN_H && !defined XAPIAN_LIB_BUILD
# error Never use <xapian/error.h> directly; include <xapian.h> instead.
#endif

#include <string>
#include <xapian/attributes.h>
#include <xapian/visibility.h>

namespace Xapian {

/** All exceptions thrown by Xapian are subclasses of Xapian::Error.
 *
 *  This class can not be instantiated directly - instead a subclass should
 *  be used.
 */
class XAPIAN_VISIBILITY_DEFAULT Error {
    /// Message giving details of the error, intended for human consumption.
    std::string msg;

    /** Optional context information.
     *
     *  This context is intended for machine use (for example to know which
     *  remote server an error came from), but it is typically a
     *  plain-text string, and so also fit for human consumption.
     */
    std::string context;

    /** The error string derived from my_errno.
     *
     *  This string is generated from my_errno lazily.
     */
    mutable std::string error_string;

    /// The type of this error (e.g. DocNotFoundError.)
    const char * type;

    /** Optional value of 'errno' associated with this error.
     *
     *  If no value is associated, this member variable will be 0.
     *
     *  On UNIX, if this value is < 0, it's an error code returned from
     *  getaddrinfo() (negated if such error codes are positive).
     *
     *  On Windows, if this value is < 0, it's a negated Windows error code
     *  (as given by GetLastError()), while if it is >= WSABASEERR then it is a
     *  WinSock error code (as given by WSAGetLastError()).  Prior to Xapian
     *  1.2.20 and 1.3.3, WSAGetLastError() codes were also negated.
     *
     *  NB We don't just call this member "errno" to avoid problems on
     *  platforms where errno is a preprocessor macro.
     */
    int my_errno;

    /// Don't allow assignment of the base class.
    void operator=(const Error &o);

  protected:
    /** @private @internal
     *  @brief Constructor for use by constructors of derived classes.
     */
    Error(std::string_view msg_, std::string_view context_,
          const char* type_, const char* error_string_);

    /** @private @internal
     *  @brief Constructor for use by constructors of derived classes.
     */
    Error(std::string_view msg_, std::string_view context_,
          const char* type_, int errno_)
        : msg(msg_), context(context_), error_string(), type(type_),
          my_errno(errno_) { }

  public:
    /** Default copy constructor.
     *
     *  We explicitly specify this to avoid warnings from GCC 9 (from
     *  -Wdeprecated-copy which is enabled by -Wextra).
     */
    Error(const Error&) = default;

    /// The type of this error (e.g. "DocNotFoundError".)
    const char* get_type() const noexcept {
        return type + 1;
    }

    /// Message giving details of the error, intended for human consumption.
    const std::string& get_msg() const noexcept {
        return msg;
    }

    /** Optional context information.
     *
     *  This context is intended for machine use (for example to know which
     *  remote server an error came from), but it is typically a
     *  plain-text string, and so also fit for human consumption.
     */
    const std::string& get_context() const noexcept {
        return context;
    }

    /** Returns any system error string associated with this exception.
     *
     *  The system error string may come from errno, h_errno (on UNIX), or
     *  GetLastError() (on MS Windows).  If there is no associated system
     *  error string, NULL is returned.
     */
    const char * get_error_string() const;

    /// Return a string describing this object.
    std::string get_description() const;
};
EOF

print DISPATCH <<'EOF';

/* Note that this file isn't an external header - it's located in
 * include/xapian in the source tree because it's generated so this
 * is the simplest way to make inclusion work in a VPATH build.
 */

// DOXYGEN gets confused by this header-with-code.
#ifndef DOXYGEN
EOF

# Format description for embedding in C /* ... */ comment.
sub c_format_description {
    my $d = shift;
    if (defined $d && $d ne '') {
        $d =~ s!^! *  !mg;
        $d =~ s! +$!!mg;
        $d = " *\n" . $d;
    } else {
        $d = '';
    }
    return $d;
}

for (@baseclasses) {
    chomp;
    my ($class, $parent, $synopsis, $description) = @{$_};
    $description = c_format_description($description);
    print HDR <<EOF;

/** $synopsis
$description */
class XAPIAN_VISIBILITY_DEFAULT $class : public $parent {
  protected:
    /** \@private \@internal
     *  \@brief Constructor for use by constructors of derived classes.
     */
    $class(std::string_view msg_, std::string_view context_, const char* type_, const char* error_string_)
        : $parent(msg_, context_, type_, error_string_) {}

    /** \@private \@internal
     *  \@brief Constructor for use by constructors of derived classes.
     */
    $class(std::string_view msg_, std::string_view context_, const char* type_, int errno_)
        : $parent(msg_, context_, type_, errno_) {}
};
EOF
}

for (@classes) {
    chomp;
    my ($class, $parent, $synopsis, $description) = @{$_};
    $description = c_format_description($description);
    my $code = sprintf('\%03o', $classcode{$class});

    print DISPATCH "case '$code': throw Xapian::$class(msg, context, error_string);\n";

    print HDR <<EOF;

/** $synopsis
$description */
class XAPIAN_VISIBILITY_DEFAULT $class : public $parent {
  public:
    /** \@private \@internal
     *  \@brief Private constructor for use by remote backend.
     *
     *  \@param error_string_   Optional string describing error.  May be NULL.
     */
    $class(std::string_view msg_, std::string_view context_, const char* error_string_)
        : $parent(msg_, context_, "$code$class", error_string_) {}
    /** General purpose constructor.
     *
     *  \@param msg_            Message giving details of the error, intended
     *                          for human consumption.
     *  \@param context_        Optional context information for this error.
     *  \@param errno_          Optional errno value associated with this error.
     */
    explicit $class(std::string_view msg_, std::string_view context_ = {}, int errno_ = 0)
        : $parent(msg_, context_, "$code$class", errno_) {}
    /** Construct from message and errno value.
     *
     *  \@param msg_            Message giving details of the error, intended
     *                          for human consumption.
     *  \@param errno_          Optional errno value associated with this error.
     */
    $class(std::string_view msg_, int errno_)
        : $parent(msg_, std::string(), "$code$class", errno_) {}
  protected:
    /** \@private \@internal
     *  \@brief Constructor for use by constructors of derived classes.
     */
    $class(std::string_view msg_, std::string_view context_, const char* type_, const char* error_string_)
        : $parent(msg_, context_, type_, error_string_) {}

    /** \@private \@internal
     *  \@brief Constructor for use by constructors of derived classes.
     */
    $class(std::string_view msg_, std::string_view context_, const char* type_, int errno_)
        : $parent(msg_, context_, type_, errno_) {}
};
EOF
}

print HDR <<'EOF';

}

#endif /* XAPIAN_INCLUDED_ERROR_H */
EOF

print DISPATCH <<'EOF';
#endif /* DOXYGEN */
EOF

close HDR or die $!;
close DISPATCH or die $!;

# vim: set syntax=perl:
