2
0
mirror of https://github.com/boostorg/leaf.git synced 2026-01-19 16:32:08 +00:00
Files
leaf/test/json_encoder_boost_test.cpp
2026-01-13 15:34:49 -05:00

425 lines
14 KiB
C++

// 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 <boost/leaf/handle_errors.hpp>
# include <boost/leaf/result.hpp>
# include <boost/leaf/diagnostics.hpp>
# include <boost/leaf/common.hpp>
# include <boost/leaf/on_error.hpp>
# include <boost/leaf/serialization/json_encoder_boost.hpp>
#endif
#include <boost/json.hpp>
#include <iomanip>
#include <iostream>
#include <stdexcept>
#if BOOST_LEAF_CFG_STD_SYSTEM_ERROR
# include <system_error>
#endif
#include "lightweight_test.hpp"
#if BOOST_LEAF_CFG_STD_STRING
namespace leaf = boost::leaf;
using output_encoder = leaf::serialization::json_encoder_boost;
namespace boost { namespace leaf {
namespace serialization {
template <class Handle, class T>
void serialize(Handle & h, T const & x, char const * name)
{
h.dispatch(
[&](json_encoder_boost & 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 <int N>
struct my_error
{
int code;
char const * message;
friend void tag_invoke(boost::json::value_from_tag, boost::json::value & jv, my_error const & e)
{
jv.emplace_object();
jv.as_object()["code"] = e.code;
jv.as_object()["message"] = e.message;
}
friend std::ostream & operator<<(std::ostream & os, my_error const & e)
{
return os << "code=" << e.code << ", message=" << e.message;
}
};
leaf::result<void> 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(boost::json::value const & j, bool has_source_location)
{
BOOST_TEST(boost::json::value_to<int>(j.at("boost::leaf::error_id")) > 0);
auto const & e1j = j.at("my_error<1>");
BOOST_TEST_EQ(boost::json::value_to<int>(e1j.at("code")), 1);
BOOST_TEST_EQ(boost::json::value_to<std::string>(e1j.at("message")), "error one");
if( has_source_location )
{
auto const & loc = j.at("boost::leaf::e_source_location");
BOOST_TEST(!boost::json::value_to<std::string>(loc.at("file")).empty());
BOOST_TEST(boost::json::value_to<int>(loc.at("line")) > 0);
BOOST_TEST(!boost::json::value_to<std::string>(loc.at("function")).empty());
}
BOOST_TEST(j.as_object().find("my_error<2>") == j.as_object().end());
}
void check_diagnostic_details(boost::json::value const & j, bool has_source_location)
{
BOOST_TEST(boost::json::value_to<int>(j.at("boost::leaf::error_id")) > 0);
auto const & e1j = j.at("my_error<1>");
BOOST_TEST_EQ(boost::json::value_to<int>(e1j.at("code")), 1);
BOOST_TEST_EQ(boost::json::value_to<std::string>(e1j.at("message")), "error one");
if( has_source_location )
{
auto const & loc = j.at("boost::leaf::e_source_location");
BOOST_TEST(!boost::json::value_to<std::string>(loc.at("file")).empty());
BOOST_TEST(boost::json::value_to<int>(loc.at("line")) > 0);
BOOST_TEST(!boost::json::value_to<std::string>(loc.at("function")).empty());
}
if( BOOST_LEAF_CFG_CAPTURE )
{
BOOST_TEST_EQ(boost::json::value_to<int>(j.at("int")), 42);
auto const & e2j = j.at("my_error<2>");
BOOST_TEST_EQ(boost::json::value_to<int>(e2j.at("code")), 2);
BOOST_TEST_EQ(boost::json::value_to<std::string>(e2j.at("message")), "error two");
auto const & ej = j.at("boost::leaf::e_errno");
BOOST_TEST_EQ(boost::json::value_to<int>(ej.at("errno")), ENOENT);
BOOST_TEST(!boost::json::value_to<std::string>(ej.at("strerror")).empty());
BOOST_TEST_EQ(boost::json::value_to<std::string>(j.at("boost::leaf::e_api_function")), "my_api_function");
if( BOOST_LEAF_CFG_STD_SYSTEM_ERROR )
{
auto const & ecj = j.at("std::error_code");
BOOST_TEST_EQ(boost::json::value_to<int>(ecj.at("value")), static_cast<int>(std::errc::invalid_argument));
BOOST_TEST(!boost::json::value_to<std::string>(ecj.at("category")).empty());
BOOST_TEST(!boost::json::value_to<std::string>(ecj.at("message")).empty());
auto const & econdj = j.at("std::error_condition");
BOOST_TEST_EQ(boost::json::value_to<int>(econdj.at("value")), static_cast<int>(std::errc::io_error));
BOOST_TEST(!boost::json::value_to<std::string>(econdj.at("category")).empty());
BOOST_TEST(!boost::json::value_to<std::string>(econdj.at("message")).empty());
auto const & mecj = j.at("my_error_code");
BOOST_TEST_EQ(boost::json::value_to<int>(mecj.at("value")), static_cast<int>(std::errc::invalid_argument));
BOOST_TEST(!boost::json::value_to<std::string>(mecj.at("category")).empty());
BOOST_TEST(!boost::json::value_to<std::string>(mecj.at("message")).empty());
}
}
else
{
BOOST_TEST(j.as_object().find("int") == j.as_object().end());
BOOST_TEST(j.as_object().find("my_error<2>") == j.as_object().end());
BOOST_TEST(j.as_object().find("boost::leaf::e_errno") == j.as_object().end());
BOOST_TEST(j.as_object().find("boost::leaf::e_api_function") == j.as_object().end());
BOOST_TEST(j.as_object().find("std::error_code") == j.as_object().end());
BOOST_TEST(j.as_object().find("std::error_condition") == j.as_object().end());
}
}
#ifndef BOOST_LEAF_NO_EXCEPTIONS
void check_exception(boost::json::value const & j)
{
auto const & exj = j.at("std::exception");
BOOST_TEST(!boost::json::value_to<std::string>(exj.at("dynamic_type")).empty());
BOOST_TEST(!boost::json::value_to<std::string>(exj.at("what")).empty());
}
#endif
int main()
{
{
boost::json::value 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" << boost::json::serialize(j) << std::endl;
check_diagnostic_info(j, true);
}
{
boost::json::value 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" << boost::json::serialize(j) << std::endl;
check_diagnostic_details(j, true);
}
#ifndef BOOST_LEAF_NO_EXCEPTIONS
{
boost::json::value 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" << boost::json::serialize(j) << std::endl;
check_diagnostic_info(j, true);
check_exception(j);
}
{
boost::json::value 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" << boost::json::serialize(j) << std::endl;
check_diagnostic_details(j, true);
check_exception(j);
}
{
boost::json::value 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" << boost::json::serialize(j) << std::endl;
check_diagnostic_info(j, false);
}
{
boost::json::value 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" << boost::json::serialize(j) << std::endl;
check_diagnostic_details(j, false);
}
{
boost::json::value j;
leaf::try_handle_all(
[]() -> leaf::result<void>
{
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" << boost::json::serialize(j) << std::endl;
auto const & ep = j.at("my_exception_ptr");
auto type = boost::json::value_to<std::string>(ep.at("dynamic_type"));
auto what = boost::json::value_to<std::string>(ep.at("what"));
BOOST_TEST(type.find("std::runtime_error") != std::string::npos);
BOOST_TEST_EQ(what, "test exception");
}
{
boost::json::value j;
leaf::try_handle_all(
[]() -> leaf::result<void>
{
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" << boost::json::serialize(j) << std::endl;
auto const & ep = j.at("my_exception_ptr");
auto type = boost::json::value_to<std::string>(ep.at("dynamic_type"));
auto what = boost::json::value_to<std::string>(ep.at("what"));
BOOST_TEST_EQ(type, "<<unknown>>");
BOOST_TEST_EQ(what, "N/A");
}
#endif
{
boost::json::value j;
leaf::result<int> r = 42;
BOOST_TEST(r);
output_encoder e{j};
r.output_to(e);
std::cout << __LINE__ << " result<int> success JSON output:\n" << boost::json::serialize(j) << std::endl;
BOOST_TEST_EQ(boost::json::value_to<int>(j.at("int")), 42);
}
{
boost::json::value j;
leaf::result<int> r = leaf::new_error();
BOOST_TEST(!r);
output_encoder e{j};
r.output_to(e);
std::cout << __LINE__ << " result<int> error JSON output:\n" << boost::json::serialize(j) << std::endl;
BOOST_TEST(boost::json::value_to<int>(j.at("boost::leaf::error_id")) > 0);
}
#if BOOST_LEAF_CFG_CAPTURE
{
boost::json::value j;
leaf::result<int> r = leaf::try_capture_all(
[]() -> leaf::result<int>
{
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<int> captured error JSON output:\n" << boost::json::serialize(j) << std::endl;
BOOST_TEST(boost::json::value_to<int>(j.at("boost::leaf::error_id")) > 0);
auto const & e1j = j.at("my_error<1>");
BOOST_TEST_EQ(boost::json::value_to<int>(e1j.at("code")), 1);
BOOST_TEST_EQ(boost::json::value_to<std::string>(e1j.at("message")), "error one");
auto const & e2j = j.at("my_error<2>");
BOOST_TEST_EQ(boost::json::value_to<int>(e2j.at("code")), 2);
BOOST_TEST_EQ(boost::json::value_to<std::string>(e2j.at("message")), "error two");
}
#endif
return boost::report_errors();
}
#else
int main()
{
return 0;
}
#endif