# generate-python-exceptions: generate error handling code for Python bindings
my $copyright = <<'END';
 Copyright (C) 2003,2004,2006,2007,2008,2009,2012 Olly Betts
 Copyright (C) 2007 Lemur Consulting Ltd
 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/>.
END

use strict;
use exception_data;

my @allclasses = (@baseclasses, @classes);

my @posting_source_virtual_methods = qw(
    get_termfreq_min
    get_termfreq_est
    get_termfreq_max
    get_maxweight
    get_weight
    next
    skip_to
    check
    at_end
    get_docid
    get_description
    init
    name
);

my @matchspy_virtual_methods = qw(
    name
    merge_results
    get_description
);

open FD, ">except.i" or die $!;

$copyright =~ s/^/ */mg;

print FD <<"EOF";
/** \@file
 * \@brief Custom Python exception handling.
 */
/* Warning: This file is generated by $0
 * - do not modify directly!
 *
$copyright */

EOF

print FD <<'EOF';
namespace Xapian {

%exceptionclass Error;
%ignore Error::get_description;
%extend Error {
    std::string __str__() const {
        std::string desc($self->get_msg());
        if (!$self->get_context().empty()) {
            desc += " (context: ";
            desc += $self->get_context();
            desc += ')';
        }
        if ($self->get_error_string()) {
            desc += " (";
            desc += $self->get_error_string();
            desc += ')';
        }
        return desc;
    }
}
EOF

for (@allclasses) {
    my ($class) = @{$_};
    print FD "%exceptionclass $class;\n";
}

print FD <<'EOF';
}
%ignore Xapian::Error::Error(const Error&);
%include "xapian/error.h"

%{
namespace Xapian {
SWIGEXPORT void SetPythonException() {
    try {
        throw;
    } catch (Swig::DirectorException &) {
        /* This happens if a director raised an exception.  The standard SWIG
         * director exception handling code sets the Python error state if
         * necessary, so we don't need to do anything. */
EOF

for (reverse @allclasses) {
    my ($class) = @{$_};
    print FD <<"EOF";
    } catch (const Xapian::$class &e) {
        SWIG_Python_Raise(SWIG_NewPointerObj((new Xapian::$class(e)),
                                             SWIGTYPE_p_Xapian__$class,
                                             SWIG_POINTER_OWN),
                          "Xapian::$class",
                          SWIGTYPE_p_Xapian__$class);
EOF
}

print FD <<'EOF';
    } catch (const Xapian::Error &e) {
        SWIG_Python_Raise(SWIG_NewPointerObj((new Xapian::Error(e)),
                                             SWIGTYPE_p_Xapian__Error,
                                             SWIG_POINTER_OWN),
                          "Xapian::Error",
                          SWIGTYPE_p_Xapian__Error);
    } catch (const std::exception& e) {
        SWIG_Error(SWIG_RuntimeError, e.what());
    } catch (...) {
        SWIG_Error(SWIG_UnknownError, "unknown error in Xapian");
    }
}
}
%}

/* Functions and methods which are marked as "nothrow": */
EOF

chdir($INC[0]);
exception_data::for_each_nothrow(sub { print FD "%exception $_[0];\n" });

print FD <<'EOF';

%exception {
    try {
        $action
    } catch (...) {
        Xapian::SetPythonException();
        SWIG_fail;
    }
}

/* If a Python error is raised by a call to a director function, the following
 * code should cause a C++ exception to be thrown.
 */
%feature("director:except") {
    if ($error != NULL) {
        throw Swig::DirectorMethodException();
    }
}

/* vim:syntax=cpp:set noexpandtab: */
EOF

close FD or die $!;
