# 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 $!;
