// Copyright 2018-2026 Emil Dotchevski and Reverge Studios, Inc. // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #ifdef BOOST_LEAF_TEST_SINGLE_HEADER # include "leaf.hpp" #else # include # include # include # include # include # include #endif #include "nlohmann/json.hpp" #include #include #include #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR # include #endif #include "lightweight_test.hpp" #if BOOST_LEAF_CFG_STD_STRING namespace leaf = boost::leaf; using output_encoder = leaf::serialization::json_encoder_nlohmann; namespace boost { namespace leaf { namespace serialization { template void serialize(Handle & h, T const & x, char const * name) { h.dispatch( [&](json_encoder_nlohmann & e) { output_at(e, x, name); }, [&](json_encoder_nlohmann & e) { output_at(e, x, name); } ); } } } } struct my_exception { }; struct my_exception_ptr { std::exception_ptr value; }; struct my_error_code { std::error_code value; }; template struct my_error { int code; char const * message; template friend void to_json(Json & j, my_error const & e) { j["code"] = e.code; j["message"] = e.message; } friend std::ostream & operator<<(std::ostream & os, my_error const & e) { return os << "code=" << e.code << ", message=" << e.message; } }; leaf::result fail() { return BOOST_LEAF_NEW_ERROR( #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR std::make_error_code(std::errc::invalid_argument), std::make_error_condition(std::errc::io_error), my_error_code{std::make_error_code(std::errc::invalid_argument)}, #endif 42, my_error<1>{1, "error one"}, my_error<2>{2, "error two"}, leaf::e_errno{ENOENT}, leaf::e_api_function{"my_api_function"} ); } #ifndef BOOST_LEAF_NO_EXCEPTIONS void leaf_throw() { BOOST_LEAF_THROW_EXCEPTION( #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR std::make_error_code(std::errc::invalid_argument), std::make_error_condition(std::errc::io_error), my_error_code{std::make_error_code(std::errc::invalid_argument)}, #endif 42, my_error<1>{1, "error one"}, my_error<2>{2, "error two"}, leaf::e_errno{ENOENT}, leaf::e_api_function{"my_api_function"} ); } void throw_() { auto load = leaf::on_error( #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR std::make_error_code(std::errc::invalid_argument), std::make_error_condition(std::errc::io_error), my_error_code{std::make_error_code(std::errc::invalid_argument)}, #endif 42, my_error<1>{1, "error one"}, my_error<2>{2, "error two"}, leaf::e_errno{ENOENT}, leaf::e_api_function{"my_api_function"} ); throw my_exception{}; } #endif void check_diagnostic_info(nlohmann::ordered_json const & j, bool has_source_location) { BOOST_TEST(j["boost::leaf::error_id"].get() > 0); auto const & e1j = j["my_error<1>"]; BOOST_TEST_EQ(e1j["code"].get(), 1); BOOST_TEST_EQ(e1j["message"].get(), "error one"); if( has_source_location ) { auto const & loc = j["boost::leaf::e_source_location"]; BOOST_TEST(!loc["file"].get().empty()); BOOST_TEST(loc["line"].get() > 0); BOOST_TEST(!loc["function"].get().empty()); } BOOST_TEST(!j.contains("my_error<2>")); } void check_diagnostic_details(nlohmann::ordered_json const & j, bool has_source_location) { BOOST_TEST(j["boost::leaf::error_id"].get() > 0); auto const & e1j = j["my_error<1>"]; BOOST_TEST_EQ(e1j["code"].get(), 1); BOOST_TEST_EQ(e1j["message"].get(), "error one"); if( has_source_location ) { auto const & loc = j["boost::leaf::e_source_location"]; BOOST_TEST(!loc["file"].get().empty()); BOOST_TEST(loc["line"].get() > 0); BOOST_TEST(!loc["function"].get().empty()); } if( BOOST_LEAF_CFG_CAPTURE ) { BOOST_TEST_EQ(j["int"].get(), 42); auto const & e2j = j["my_error<2>"]; BOOST_TEST_EQ(e2j["code"].get(), 2); BOOST_TEST_EQ(e2j["message"].get(), "error two"); auto const & ej = j["boost::leaf::e_errno"]; BOOST_TEST_EQ(ej["errno"].get(), ENOENT); BOOST_TEST(!ej["strerror"].get().empty()); BOOST_TEST_EQ(j["boost::leaf::e_api_function"].get(), "my_api_function"); if( BOOST_LEAF_CFG_STD_SYSTEM_ERROR ) { auto const & ecj = j["std::error_code"]; BOOST_TEST_EQ(ecj["value"].get(), static_cast(std::errc::invalid_argument)); BOOST_TEST(!ecj["category"].get().empty()); BOOST_TEST(!ecj["message"].get().empty()); auto const & econdj = j["std::error_condition"]; BOOST_TEST_EQ(econdj["value"].get(), static_cast(std::errc::io_error)); BOOST_TEST(!econdj["category"].get().empty()); BOOST_TEST(!econdj["message"].get().empty()); auto const & mecj = j["my_error_code"]; BOOST_TEST_EQ(mecj["value"].get(), static_cast(std::errc::invalid_argument)); BOOST_TEST(!mecj["category"].get().empty()); BOOST_TEST(!mecj["message"].get().empty()); } } else { BOOST_TEST(!j.contains("int")); BOOST_TEST(!j.contains("my_error<2>")); BOOST_TEST(!j.contains("boost::leaf::e_errno")); BOOST_TEST(!j.contains("boost::leaf::e_api_function")); BOOST_TEST(!j.contains("std::error_code")); BOOST_TEST(!j.contains("std::error_condition")); } } #ifndef BOOST_LEAF_NO_EXCEPTIONS void check_exception(nlohmann::ordered_json const & j) { auto const & exj = j["std::exception"]; BOOST_TEST(!exj["dynamic_type"].get().empty()); BOOST_TEST(!exj["what"].get().empty()); } #endif int main() { { nlohmann::ordered_json j; leaf::try_handle_all( [] { return fail(); }, [&j](leaf::diagnostic_info const & di, my_error<1> const * e1) { BOOST_TEST(e1 != nullptr); output_encoder e{j}; di.output_to(e); } ); std::cout << __LINE__ << " diagnostic_info JSON output:\n" << std::setw(2) << j << std::endl; check_diagnostic_info(j, true); } { nlohmann::ordered_json j; leaf::try_handle_all( [] { return fail(); }, [&j](leaf::diagnostic_details const & dd, my_error<1> const * e1) { BOOST_TEST(e1 != nullptr); output_encoder e{j}; dd.output_to(e); } ); std::cout << __LINE__ << " diagnostic_details JSON output:\n" << std::setw(2) << j << std::endl; check_diagnostic_details(j, true); } #ifndef BOOST_LEAF_NO_EXCEPTIONS { nlohmann::ordered_json j; leaf::try_catch( [] { leaf_throw(); }, [&j](leaf::diagnostic_info const & di, my_error<1> const * e1) { BOOST_TEST(e1 != nullptr); output_encoder e{j}; di.output_to(e); } ); std::cout << __LINE__ << " leaf_throw diagnostic_info JSON output:\n" << std::setw(2) << j << std::endl; check_diagnostic_info(j, true); check_exception(j); } { nlohmann::ordered_json j; leaf::try_catch( [] { leaf_throw(); }, [&j](leaf::diagnostic_details const & dd, my_error<1> const * e1) { BOOST_TEST(e1 != nullptr); output_encoder e{j}; dd.output_to(e); } ); std::cout << __LINE__ << " leaf_throw diagnostic_details JSON output:\n" << std::setw(2) << j << std::endl; check_diagnostic_details(j, true); check_exception(j); } { nlohmann::ordered_json j; leaf::try_catch( [] { throw_(); }, [&j](leaf::diagnostic_info const & di, my_error<1> const * e1) { BOOST_TEST(e1 != nullptr); output_encoder e{j}; di.output_to(e); } ); std::cout << __LINE__ << " throw_ diagnostic_info JSON output:\n" << std::setw(2) << j << std::endl; check_diagnostic_info(j, false); } { nlohmann::ordered_json j; leaf::try_catch( [] { throw_(); }, [&j](leaf::diagnostic_details const & dd, my_error<1> const * e1) { BOOST_TEST(e1 != nullptr); output_encoder e{j}; dd.output_to(e); } ); std::cout << __LINE__ << " throw_ diagnostic_details JSON output:\n" << std::setw(2) << j << std::endl; check_diagnostic_details(j, false); } { nlohmann::ordered_json j; leaf::try_handle_all( []() -> leaf::result { return leaf::new_error(my_exception_ptr{std::make_exception_ptr(std::runtime_error("test exception"))}); }, [&j](leaf::diagnostic_details const & dd, my_exception_ptr *) { output_encoder e{j}; dd.output_to(e); } ); std::cout << __LINE__ << " std::exception_ptr JSON output:\n" << std::setw(2) << j << std::endl; auto const & ep = j["my_exception_ptr"]; std::string type = ep["dynamic_type"].get(); std::string what = ep["what"].get(); BOOST_TEST(type.find("std::runtime_error") != std::string::npos); BOOST_TEST_EQ(what, "test exception"); } { nlohmann::ordered_json j; leaf::try_handle_all( []() -> leaf::result { return leaf::new_error(my_exception_ptr{std::make_exception_ptr(42)}); }, [&j](leaf::diagnostic_details const & dd, my_exception_ptr *) { output_encoder e{j}; dd.output_to(e); } ); std::cout << __LINE__ << " non-std::exception_ptr JSON output:\n" << std::setw(2) << j << std::endl; auto const & ep = j["my_exception_ptr"]; std::string type = ep["dynamic_type"].get(); std::string what = ep["what"].get(); BOOST_TEST_EQ(type, "<>"); BOOST_TEST_EQ(what, "N/A"); } #endif { nlohmann::ordered_json j; leaf::result r = 42; BOOST_TEST(r); output_encoder e{j}; r.output_to(e); std::cout << __LINE__ << " result success JSON output:\n" << std::setw(2) << j << std::endl; BOOST_TEST_EQ(j["int"].get(), 42); } { nlohmann::ordered_json j; leaf::result r = leaf::new_error(); BOOST_TEST(!r); output_encoder e{j}; r.output_to(e); std::cout << __LINE__ << " result error JSON output:\n" << std::setw(2) << j << std::endl; BOOST_TEST(j["boost::leaf::error_id"].get() > 0); } #if BOOST_LEAF_CFG_CAPTURE { nlohmann::ordered_json j; leaf::result r = leaf::try_capture_all( []() -> leaf::result { return leaf::new_error(my_error<1>{1, "error one"}, my_error<2>{2, "error two"}); } ); BOOST_TEST(!r); output_encoder e{j}; r.output_to(e); std::cout << __LINE__ << " result captured error JSON output:\n" << std::setw(2) << j << std::endl; BOOST_TEST(j["boost::leaf::error_id"].get() > 0); auto const & e1j = j["my_error<1>"]; BOOST_TEST_EQ(e1j["code"].get(), 1); BOOST_TEST_EQ(e1j["message"].get(), "error one"); auto const & e2j = j["my_error<2>"]; BOOST_TEST_EQ(e2j["code"].get(), 2); BOOST_TEST_EQ(e2j["message"].get(), "error two"); } #endif return boost::report_errors(); } #else int main() { return 0; } #endif