A tile that fails to decode is reported with EXIT_MVT, while a tile that
protozero throws on is reported with EXIT_PROTOBUF. Now that decoding
failures are raised as a tippecanoe_error rather than exiting on the
spot, the catch that was written for protozero's exceptions catches
tippecanoe's own as well, since tippecanoe_error is a std::exception:
mvt_tile::decode() throwing "Tile decompression failed" came back out as
EXIT_PROTOBUF and "PBF decoding error in tile z/x/y".
Catch tippecanoe_error first at the four places that wrap a decode in a
try. In overzoom() and in tippecanoe-decode it can be rethrown for
main() to report. In tile-join it can't: append_tile() runs on a worker
thread, where an escaping exception would call std::terminate() instead
of exiting, so report it and exit with its own status there.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DuCSVGssWNUCpVEE8Yp5HT
The code reachable from overzooming a tile no longer calls exit(). It
reports errors by throwing tippecanoe_error, and owns its FILE * and
json_pull * handles through RAII wrappers so that nothing leaks when an
error unwinds the stack.
New files:
* errors.hpp/errors.cpp: the tippecanoe_error exception class, plus
throw_tippecanoe_error() and throw_perror() helpers.
* raii.hpp: unique_file and unique_json_pull, both std::unique_ptr
aliases, for FILE * and json_pull *.
exit() becomes throw in clip.cpp, mvt.cpp, mvt.hpp, read_json.cpp,
evaluator.cpp, text.cpp, geometry.cpp, attribute.cpp, and
write_json.hpp. Each tool's main() is now a try/catch around an
inner_main(), so the command-line tools exit with the status they
always did.
Along the way this fixes a z_stream leaked when compress() failed, an
exit() that could run from json_writer's destructor during unwinding,
and an exit() in mvt_value's hash function.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DuCSVGssWNUCpVEE8Yp5HT